Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Invalid bundle in watch mode when importing a script of type="module" on a manifest version 2 extension page #10085

Open
david-tejada opened this issue Feb 7, 2025 · 2 comments

Comments

@david-tejada
Copy link

🐛 bug report

Parcel produces an invalid bundle in watch mode when importing a script of type="module" on a manifest version 2 extension page. The bundle works fine when building and using manifest version 3.

🎛 Configuration (.babelrc, package.json, cli command)

{
	"extends": "@parcel/config-webextension",
	"optimizers": {
		"*.{js,ts}": [
			"@parcel/optimizer-terser"
		]
	}
}

And the command used:

parcel watch src/manifests/firefox/manifest.json --dist-dir dist/firefox --no-cache --no-content-hash --no-hmr --no-cache --no-source-maps

🤔 Expected Behavior

The bundle should work fine in watch mode.

😯 Current Behavior

The bundle produced has an empty entry array and a null main entry. If I manually add those, the bundle works fine.

💁 Possible Solution

🔦 Context

💻 Code Sample

This is the bundled produced:

// modules are defined as an array
// [ module function, map of requires ]
//
// map of requires is short require name -> numeric require
//
// anything defined in a previous bundle is accessed via the
// orig method which is the require for previous bundles

(function (modules, entry, mainEntry, parcelRequireName, globalName) {
	/* eslint-disable no-undef */
	var globalObject =
		typeof globalThis !== "undefined"
			? globalThis
			: typeof self !== "undefined"
				? self
				: typeof window !== "undefined"
					? window
					: typeof global !== "undefined"
						? global
						: {};
	/* eslint-enable no-undef */

	// Save the require from previous bundle to this closure if any
	var previousRequire =
		typeof globalObject[parcelRequireName] === "function" &&
		globalObject[parcelRequireName];

	var cache = previousRequire.cache || {};
	// Do not use `require` to prevent Webpack from trying to bundle this call
	var nodeRequire =
		typeof module !== "undefined" &&
		typeof module.require === "function" &&
		module.require.bind(module);

	function newRequire(name, jumped) {
		if (!cache[name]) {
			if (!modules[name]) {
				// if we cannot find the module within our internal map or
				// cache jump to the current global require ie. the last bundle
				// that was added to the page.
				var currentRequire =
					typeof globalObject[parcelRequireName] === "function" &&
					globalObject[parcelRequireName];
				if (!jumped && currentRequire) {
					return currentRequire(name, true);
				}

				// If there are other bundles on this page the require from the
				// previous one is saved to 'previousRequire'. Repeat this as
				// many times as there are bundles until the module is found or
				// we exhaust the require chain.
				if (previousRequire) {
					return previousRequire(name, true);
				}

				// Try the node require function if it exists.
				if (nodeRequire && typeof name === "string") {
					return nodeRequire(name);
				}

				var err = new Error("Cannot find module '" + name + "'");
				err.code = "MODULE_NOT_FOUND";
				throw err;
			}

			localRequire.resolve = resolve;
			localRequire.cache = {};

			var module = (cache[name] = new newRequire.Module(name));

			modules[name][0].call(
				module.exports,
				localRequire,
				module,
				module.exports,
				globalObject
			);
		}

		return cache[name].exports;

		function localRequire(x) {
			var res = localRequire.resolve(x);
			return res === false ? {} : newRequire(res);
		}

		function resolve(x) {
			var id = modules[name][1][x];
			return id != null ? id : x;
		}
	}

	function Module(moduleName) {
		this.id = moduleName;
		this.bundle = newRequire;
		this.exports = {};
	}

	newRequire.isParcelRequire = true;
	newRequire.Module = Module;
	newRequire.modules = modules;
	newRequire.cache = cache;
	newRequire.parent = previousRequire;
	newRequire.register = function (id, exports) {
		modules[id] = [
			function (require, module) {
				module.exports = exports;
			},
			{},
		];
	};

	Object.defineProperty(newRequire, "root", {
		get: function () {
			return globalObject[parcelRequireName];
		},
	});

	globalObject[parcelRequireName] = newRequire;

	for (var i = 0; i < entry.length; i++) {
		newRequire(entry[i]);
	}

	if (mainEntry) {
		// Expose entry point to Node, AMD or browser globals
		// Based on https://github.com/ForbesLindesay/umd/blob/master/template.js
		var mainExports = newRequire(mainEntry);

		// CommonJS
		if (typeof exports === "object" && typeof module !== "undefined") {
			module.exports = mainExports;

			// RequireJS
		} else if (typeof define === "function" && define.amd) {
			define(function () {
				return mainExports;
			});

			// <script>
		} else if (globalName) {
			this[globalName] = mainExports;
		}
	}
})(
	{
		lmBGF: [
			function (require, module, exports, __globalThis) {
				console.log("Hello world");
			},
			{},
		],
	},
	[],
	null,
	"parcelRequire94c2"
);

🌍 Your Environment

Software Version(s)
Parcel 2.13.3
Node 20.17.0
npm/Yarn npm 10.8.2
Operating System macOS 15.3
@ketan1829
Copy link

Here are some steps and potential solutions to address

  • you can try explicitly specifying the target environment in your package.json or .parcelrc to ensure Parcel knows it's building for a web extension.
{
  "extends": "@parcel/config-webextension",
  "transformers": {
    "*.{js,ts}": ["@parcel/transformer-babel"]
  },
  "optimizers": {
    "*.{js,ts}": ["@parcel/optimizer-terser"]
  }
}
  • ensure that the manifest.json points to the correct script files and that those files exist
{
  "manifest_version": 2,
  "name": "My Extension",
  "version": "1.0",
  "background": {
    "scripts": ["background.js"]
  },
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}

@david-tejada
Copy link
Author

I already had "extends": "@parcel/config-webextension". Your configuration gives me an error @parcel/packager-ts: TS bundles must only contain one asset.

The extension itself builds fine. The problem is not with the background or content script. It is with a page that is included in the extension in which I import a script.

<!-- index.html -->
<script src="main.ts" type="module" defer></script>
// main.ts
console.log("Hello world!")

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants