quickbundle 2.2.0 → 2.4.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/README.md +33 -4
  2. package/dist/index.js +59 -22
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -34,15 +34,44 @@ yarn add quickbundle
34
34
 
35
35
  2️⃣ Set up your package configuration (`package.json`):
36
36
 
37
+ - When exporting exclusively ESM format:
38
+
39
+ ```jsonc
40
+ {
41
+ "name": "lib", // Package name
42
+ "exports": {
43
+ ".": {
44
+ "source": "src/index.ts(x)?", // Source code entry point.
45
+ "types": "./dist/index.d.ts", // Typing output file (if defined, can increase build time). This condition should always come first after the custom `source` field definition.
46
+ "default": "./dist/index.mjs", // By default, Quickbundle will always output ESM format for the `default` field (this condition should always come last since it always matches as a generic fallback). However, take care: if both `import` and `default` fields are defined, provide the same file path, as the `import` field export instruction will be the only one considered to define the output file path.
47
+ },
48
+ "./otherModulePath": {
49
+ // ...
50
+ }
51
+ }
52
+ "scripts": {
53
+ "build": "quickbundle build", // Production mode (optimizes bundle)
54
+ "watch": "quickbundle watch", // Development mode (watches each file change)
55
+ },
56
+ // ...
57
+ }
58
+ ```
59
+
60
+ - When exporting both CommonJS (CJS) and ECMAScript Modules (ESM) format (please be aware of [dual package hazard risk](https://nodejs.org/api/packages.html#dual-package-hazard)):
61
+
37
62
  ```jsonc
38
63
  {
39
64
  "name": "lib", // Package name
40
65
  "exports": {
41
66
  ".": {
42
- "source": "src/index.ts(x)?", // Source code entrypoint
43
- "types": "./dist/index.d.ts", // Typing output file (if defined, can increase build time)
44
- "import": "./dist/index.mjs", // ESM output file
45
- "require": "./dist/index.cjs" // CommonJS output file
67
+ "source": "src/index.ts(x)?", // Source code entry point.
68
+ "types": "./dist/index.d.ts", // // Typing output file (if defined, can increase build time). This condition should always come first after the custom `source` field definition.
69
+ "require": "./dist/index.cjs", // CommonJS output file (matches when the module is loaded via require() consumer side).
70
+ "import": "./dist/index.mjs", // ESM output file (matches when the package is loaded via import or import() consumer side).
71
+ "default": "./dist/index.mjs", // By default, Quickbundle will always output ESM format for the `default` field (this condition should always come last since it always matches as a generic fallback). However, take care: if both `import` and `default` fields are defined, provide the same file path, as the `import` field export instruction will be the only one considered to define the output file path.
72
+ },
73
+ "./otherModulePath": {
74
+ // ...
46
75
  }
47
76
  }
48
77
  "scripts": {
package/dist/index.js CHANGED
@@ -16,6 +16,9 @@ import { readFile as readFile$1 } from 'node:fs/promises';
16
16
  const onLog = (_, log)=>{
17
17
  if (log.message.includes("Generated an empty chunk")) return;
18
18
  };
19
+ const isRecord = (value)=>{
20
+ return typeof value === "object" && value !== null && !Array.isArray(value);
21
+ };
19
22
 
20
23
  const build = async (configurations)=>{
21
24
  var _process_env;
@@ -60,6 +63,20 @@ const createConfigurations = (options = {
60
63
  minification: false,
61
64
  sourceMaps: false
62
65
  })=>{
66
+ return getBuildableExports().flatMap((buildableExport)=>{
67
+ return [
68
+ buildableExport.source && createMainConfig({
69
+ ...buildableExport,
70
+ source: buildableExport.source
71
+ }, options),
72
+ buildableExport.source && buildableExport.types && createTypesConfig({
73
+ source: buildableExport.source,
74
+ types: buildableExport.types
75
+ })
76
+ ].filter(Boolean);
77
+ });
78
+ };
79
+ const getBuildableExports = ()=>{
63
80
  /**
64
81
  * Entry-point resolution:
65
82
  * Following the [package entry-point specification](https://nodejs.org/api/packages.html#package-entry-points),
@@ -68,33 +85,49 @@ const createConfigurations = (options = {
68
85
  */ if (PKG.main ?? PKG.module ?? PKG.types ?? !PKG.exports) {
69
86
  throw new Error("Invalid package entry points contract. Use the recommended [`exports` field](https://nodejs.org/api/packages.html#package-entry-points) instead and, for TypeScript-based projects, update the `tsconfig.json` file to resolve it properly (`moduleResolution` must be set to `Bundler` (or `NodeNext`)).");
70
87
  }
71
- const outputEntryPointFields = [
88
+ const buildableExportFields = [
89
+ "default",
72
90
  "import",
73
91
  "require",
74
92
  "types"
75
93
  ];
76
- const output = Object.entries(PKG.exports).flatMap(([name, entryPoints])=>{
77
- if (typeof entryPoints === "string") return [];
78
- const entryPointKeys = Object.keys(entryPoints);
79
- if (entryPointKeys.includes("source")) {
80
- const hasAtLeastOneRequiredField = outputEntryPointFields.some((field)=>entryPointKeys.includes(field));
81
- if (!hasAtLeastOneRequiredField) {
82
- throw new Error(`A \`source\` field is defined without a provided output target for the \`${name}\` export. Make sure to define at least one entry point (including ${outputEntryPointFields.join(", ")})`);
94
+ let singleExport = undefined;
95
+ const output = Object.entries(PKG.exports).map(([field, value])=>{
96
+ if (isRecord(value)) {
97
+ return [
98
+ field,
99
+ value
100
+ ];
101
+ }
102
+ if ([
103
+ "source",
104
+ ...buildableExportFields
105
+ ].includes(field)) {
106
+ if (!singleExport) {
107
+ singleExport = {};
108
+ singleExport[field] = value;
109
+ return [
110
+ ".",
111
+ singleExport
112
+ ];
83
113
  }
114
+ singleExport[field] = value;
84
115
  }
85
- return [
86
- entryPoints.source && createMainConfig({
87
- ...entryPoints,
88
- source: entryPoints.source
89
- }, options),
90
- entryPoints.source && entryPoints.types && createTypesConfig({
91
- source: entryPoints.source,
92
- types: entryPoints.types
93
- })
94
- ].filter(Boolean);
95
- });
116
+ return undefined;
117
+ }).reduce((buildableExports, currentExport)=>{
118
+ if (!currentExport) return buildableExports;
119
+ const [exportField, exportValue] = currentExport;
120
+ const conditionalExportFields = Object.keys(exportValue);
121
+ if (!conditionalExportFields.includes("source")) return buildableExports;
122
+ const hasAtLeastOneRequiredField = buildableExportFields.some((entryPointField)=>conditionalExportFields.includes(entryPointField));
123
+ if (hasAtLeastOneRequiredField) {
124
+ buildableExports.push(exportValue);
125
+ return buildableExports;
126
+ }
127
+ throw new Error(`A \`source\` field is defined without an output defined for the \`${exportField}\` export. Make sure to define at least one conditional entry point (including ${buildableExportFields.map((field)=>`\`${field}\``).join(", ")})`);
128
+ }, []);
96
129
  if (output.length === 0) {
97
- throw new Error("No `source` field is set for the targeted package. If a build step is necessary, make sure to configure at least one `source` field in the package `exports` contract.");
130
+ throw new Error("No `source` field is set for the targeted package. If a build step is necessary, make sure to configure at least one `source` field in the package `exports` contract. If not, do not execute quickbundle on this package.");
98
131
  }
99
132
  return output;
100
133
  };
@@ -127,14 +160,18 @@ const getPlugins = (...customPlugins)=>{
127
160
  };
128
161
  const createMainConfig = (entryPoints, options)=>{
129
162
  const { minification, sourceMaps } = options;
163
+ const esmInput = entryPoints.import ?? entryPoints.default;
164
+ if (entryPoints.import && entryPoints.default && entryPoints.import !== entryPoints.default) {
165
+ throw new Error("Both `import` and `default` export fields have been defined but with different values. To preserve proper `default` field resolution on the consumer side (i.e. to target ESM format), make sure to provide the same file path for both fields.");
166
+ }
130
167
  const output = [
131
168
  entryPoints.require && {
132
169
  file: entryPoints.require,
133
170
  format: "cjs",
134
171
  sourcemap: sourceMaps
135
172
  },
136
- entryPoints.import && {
137
- file: entryPoints.import,
173
+ esmInput && {
174
+ file: esmInput,
138
175
  format: "es",
139
176
  sourcemap: sourceMaps
140
177
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "quickbundle",
3
- "version": "2.2.0",
3
+ "version": "2.4.0",
4
4
  "description": "The zero-configuration transpiler and bundler for the web",
5
5
  "author": {
6
6
  "name": "Ayoub Adib",