requirejs-esm 4.2.0 → 4.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.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "requirejs-esm",
3
- "version": "4.2.0",
3
+ "version": "4.4.0",
4
4
  "description": "A RequireJS plugin converting JavaScript modules from ESM to AMD.",
5
5
  "author": "Ferdinand Prantl <prantlf@gmail.com>",
6
6
  "license": "MIT",
@@ -41,7 +41,7 @@
41
41
  "demo:local": "npm run optimize:local && npm run minify:local",
42
42
  "optimize:local": "r.js -o demo-local/build.config.js",
43
43
  "minify:local": "terser -cm --ecma 2018 --comments false --source-map content=demo-local/main-built.js.map --source-map includeSources --source-map url=main-built.min.js.map -o demo-local/main-built.min.js demo-local/main-built.js",
44
- "lint": "eslint src bin perf/*.js bin test/*.js",
44
+ "lint": "biome lint src bin perf/*.js bin test/*.js",
45
45
  "fix": "npm run lint -- --fix",
46
46
  "check": "tehanu test/*.js",
47
47
  "cover": "c8 node test && c8 --no-clean ./bin/esm2requirejs.js && c8 --no-clean ./bin/esm2requirejs.js test/input/esm-import-all.js && c8 --no-clean ./bin/esm2requirejs.js dummy || c8 report -r text -r lcov | grep -Ev '(\\.\\.\\.)|(index.mjs)|(source-map.js)|(base64-vlq.js)|(base64.js)|(binary-search.js)|(mapping-list.js)|(read-wasm-browser.js)|(url-browser.js)|(wasm.js)|(regexes.js)|(public-api.js)|(All files)' && c8 check-coverage",
@@ -71,27 +71,25 @@
71
71
  "@prantlf/convert-source-map": "^3.0.2",
72
72
  "astring": "^1.9.0",
73
73
  "charcodes": "^0.2.1",
74
- "commander": "^14.0.2",
75
- "meriyah": "^7.0.0",
74
+ "commander": "^14.0.3",
75
+ "meriyah": "^7.1.0",
76
76
  "punycode": "^2.3.1",
77
77
  "source-map": "^0.8.0-beta.0",
78
78
  "tiny-glob": "^0.2.9"
79
79
  },
80
80
  "devDependencies": {
81
- "@eslint/js": "^9.39.1",
82
- "@prantlf/requirejs": "^3.3.1",
83
- "@rollup/plugin-commonjs": "^29.0.0",
81
+ "@biomejs/biome": "^2.4.16",
82
+ "@prantlf/requirejs": "^3.4.0",
83
+ "@rollup/plugin-commonjs": "^29.0.3",
84
84
  "@rollup/plugin-json": "^6.1.0",
85
85
  "@rollup/plugin-node-resolve": "^16.0.3",
86
- "c8": "^10.1.3",
87
- "eslint": "^9.39.1",
88
- "globals": "^16.5.0",
86
+ "c8": "^11.0.0",
89
87
  "lit-html": "^1",
90
- "rollup": "^4.53.3",
88
+ "rollup": "^4.61.1",
91
89
  "tehanu": "^1.0.1",
92
90
  "tehanu-repo-coco": "^1.0.1",
93
91
  "tehanu-teru": "^1.0.1",
94
- "terser": "^5.44.1"
92
+ "terser": "^5.48.0"
95
93
  },
96
94
  "keywords": [
97
95
  "requirejs-plugin",
package/src/api.d.ts CHANGED
@@ -26,7 +26,10 @@ type OnAfterUpdate = (options: AmdOptions) => boolean
26
26
  interface TransformAstOptions {
27
27
  pluginName?: string /*= 'esm'' */
28
28
  resolvePath?: ResolvePath
29
+ skipIfNoImportExport?: boolean /*= false */
29
30
  useStrict?: boolean /*= true */
31
+ splitDefaultNamedDeclarations?: boolean /*= false */
32
+ sourceMap?: boolean /*= false */
30
33
  onBeforeTransform?: OnBeforeTransform
31
34
  onAfterTransform?: OnAfterTransform
32
35
  onBeforeUpdate?: OnBeforeUpdate
package/src/api.js CHANGED
@@ -1,3 +1,8 @@
1
1
  export { resolvePath } from './resolve-path'
2
2
  export { default as transform } from './transform'
3
- export { transformAst, detectDefinesOrRequires, detectImportsAndExports } from './transformer'
3
+ export {
4
+ transformAst,
5
+ detectDefinesOrRequires,
6
+ detectImportsAndExports,
7
+ processOrSkipByComment
8
+ } from './transformer'
package/src/fetch-text.js CHANGED
@@ -21,7 +21,7 @@ if (typeof window !== 'undefined' && window.navigator && window.document) {
21
21
  xhr.send(null)
22
22
  }
23
23
  } else {
24
- const { readFileSync } = require.nodeRequire ? require.nodeRequire('fs') : require('fs')
24
+ const { readFileSync } = require.nodeRequire ? require.nodeRequire('fs') : require('node:fs')
25
25
  fetchText = (path, callback) => {
26
26
  // Asynchronous reading is not possible during the build in the optimizer.
27
27
  try {
package/src/plugin.js CHANGED
@@ -16,6 +16,8 @@ const {
16
16
  onlyAmd,
17
17
  // List of module names not to check and transform.
18
18
  skipModules = [],
19
+ // Assume AMD/UMD if there're no import or export statements.
20
+ skipIfNoImportExport,
19
21
  // Method to update paths of module dependencies, to prefix JavaScript module
20
22
  // name with `esm!`, above all.
21
23
  resolvePath,
@@ -23,6 +25,9 @@ const {
23
25
  // Do not insert `"use strict"` expression to the AMD modules. You'd set it
24
26
  // to `false` if your bundler inserts `"use strict"` to the outer scope.
25
27
  useStrict,
28
+ // Split `export default class A {}` to `class A {}; export default A`
29
+ // to trade easier AST manipulation for 100% code compatibility.
30
+ splitDefaultNamedDeclarations,
26
31
  // Boolean or object with booleans { inline, content }.
27
32
  sourceMap,
28
33
  // Enable console logging.
@@ -112,6 +117,8 @@ export default {
112
117
  resolvePath,
113
118
  /*ecmaVersion,*/
114
119
  useStrict,
120
+ skipIfNoImportExport,
121
+ splitDefaultNamedDeclarations,
115
122
  // Always produce the source maps when transpiling in the browser, otherwise
116
123
  // the debugging would me impossible. When building and bundling, check if
117
124
  // the source maps were enabled for the output.
@@ -156,7 +163,7 @@ export default {
156
163
  // compiled with esm refer it with that name and this stub will simplify
157
164
  // the module loading by skipping the plugin evaluation.
158
165
  write.asModule(`${pluginName}!${moduleName}`,
159
- '\ndefine([\'' + moduleName + '\'], res => res);\n')
166
+ `\ndefine(['${moduleName}'], res => res);\n`)
160
167
  }
161
168
  //>>excludeEnd('excludeEsm')
162
169
  }
@@ -71,7 +71,7 @@ export function resolvePath (sourcePath, currentFile, { pluginName, needsResolve
71
71
  // mapped there using the `paths` of `map` configuration properties.
72
72
  if ((sourcePath.charAt(0) === '.' && (sourcePath.charAt(1) === '/' ||
73
73
  sourcePath.charAt(1) === '.' && sourcePath.charAt(2) === '/')) &&
74
- !(needsResolve && needsResolve(sourcePath, currentFile))) {
74
+ !(needsResolve?.(sourcePath, currentFile))) {
75
75
  sourcePath = joinPath(parentDir(currentFile), sourcePath)
76
76
  if (sourcePath.endsWith('.js')) sourcePath = sourcePath.substring(0, sourcePath.length - 3)
77
77
  }
package/src/transform.js CHANGED
@@ -4,7 +4,7 @@ import { parseModule } from 'meriyah'
4
4
  import { generate } from 'astring'
5
5
  import { SourceMapGenerator } from 'source-map'
6
6
  import convert from '@prantlf/convert-source-map'
7
- import { transformAst } from './transformer'
7
+ import { transformAst, processOrSkipByComment } from './transformer'
8
8
 
9
9
  export default function transform(text, file, {
10
10
  // Allow using a different plugin alias than `esm` in the source code.
@@ -12,10 +12,15 @@ export default function transform(text, file, {
12
12
  // Method to update paths of module dependencies, to prefix JavaScript module
13
13
  // name with `esm!`, above all.
14
14
  resolvePath = originalResolvePath,
15
+ // Assume AMD/UMD if there're no import or export statements.
16
+ skipIfNoImportExport,
15
17
  // ecmaVersion = 2020,
16
18
  // Do not insert `"use strict"` expression to the AMD modules. You'd set it
17
19
  // to `false` if your bundler inserts `"use strict"` to the outer scope.
18
20
  useStrict,
21
+ // Split `export default class A {}` to `class A {}; export default A`
22
+ // to trade easier AST manipulation for 100% code compatibility.
23
+ splitDefaultNamedDeclarations,
19
24
  // Enable source maps, can be an object with booleans { inline, content }.
20
25
  // If set to true, the object will be set to { inline: true, content: true }.
21
26
  sourceMap,
@@ -26,8 +31,15 @@ export default function transform(text, file, {
26
31
  onBeforeUpdate,
27
32
  onAfterUpdate
28
33
  } = {}) {
34
+ const processOrSkip = processOrSkipByComment(text)
35
+ if (processOrSkip === false) {
36
+ return { code: text, map: null, updated: false }
37
+ } else if (processOrSkip === true) {
38
+ skipIfNoImportExport = undefined;
39
+ }
40
+
29
41
  // const ast = parse(text, { ecmaVersion, sourceType: 'module', locations: true })
30
- let ast = parseModule(text, { next: true, loc: true })
42
+ const ast = parseModule(text, { next: true, loc: true })
31
43
 
32
44
  const options = {
33
45
  sourceFileName: file,
@@ -35,6 +47,8 @@ export default function transform(text, file, {
35
47
  resolvePath,
36
48
  originalResolvePath,
37
49
  useStrict,
50
+ skipIfNoImportExport,
51
+ splitDefaultNamedDeclarations,
38
52
  onBeforeTransform,
39
53
  onAfterTransform,
40
54
  onBeforeUpdate,
@@ -0,0 +1,16 @@
1
+ // Check if one of the first 10 lines shorter than 100 characters contains
2
+ // "// requirejs-esm-skip-file" or // requirejs-esm-process-file".
3
+ export function processOrSkipByComment(text) {
4
+ for (let start = 0, i = 0; i < 10; ++i) {
5
+ const endLine = text.indexOf('\n', start)
6
+ if (endLine < 0 || endLine > 100) break
7
+ const line = text.substring(start, endLine)
8
+ const comment = /^\s*\/\//.test(line)
9
+ if (!comment) break
10
+ const directive = /^\s*\/\/\s*requirejs-esm-(skip|process)-file\s*$/.exec(line)
11
+ if (directive) {
12
+ return directive[1] === 'process'
13
+ }
14
+ start = endLine + 1
15
+ }
16
+ }
@@ -5,7 +5,7 @@ import { isValidIdentifier } from './validators'
5
5
  import { isIdentifierChar } from './identifier'
6
6
 
7
7
  export function toIdentifier(input) {
8
- input = input + ''
8
+ input = `${input}`
9
9
 
10
10
  let name = ''
11
11
  for (const c of input) {
@@ -14,9 +14,7 @@ export function toIdentifier(input) {
14
14
 
15
15
  name = name.replace(/^[-0-9]+/, '')
16
16
 
17
- name = name.replace(/[-\s]+(.)?/g, function (match, c) {
18
- return c ? c.toUpperCase() : ''
19
- })
17
+ name = name.replace(/[-\s]+(.)?/g, (_match, c) => c ? c.toUpperCase() : '')
20
18
 
21
19
  if (!isValidIdentifier(name)) {
22
20
  name = `_${name}`
@@ -87,6 +87,7 @@ export function transformEsmToAmd(program, options) {
87
87
  program
88
88
  })
89
89
 
90
+ const { splitDefaultNamedDeclarations } = options
90
91
  const { body } = program
91
92
  let { length } = body
92
93
 
@@ -148,47 +149,52 @@ export function transformEsmToAmd(program, options) {
148
149
 
149
150
  // expression after keyword default
150
151
  const { declaration } = statement
152
+ // export default X
151
153
  let exportValue = declaration
152
- let needExportExpression = true
153
-
154
- if (declaration.type === 'FunctionDeclaration') {
155
- exportValue = toExpression(exportValue)
156
- }
157
- if (declaration.type === 'ClassDeclaration') {
158
- exportValue = toExpression(exportValue)
159
- // const classNode = exportValue
160
-
161
- // if (classNode.id) {
162
- // body[i] = classNode
163
-
164
- // const className = identifier(classNode.id.name)
165
- // let exportStat
166
- // if (i + 1 === length && isOnlyDefaultExport) {
167
- // exportStat = returnStatement(identifier(className))
168
- // needReturnExport = false
169
- // } else {
170
- // exportStat = exportStatement(exportsVar, 'default', className)
171
- // }
172
-
173
- // program.pushContainer('body', [exportStat])
174
- // needExportExpression = false
175
- // } else {
176
- // exportValue = toExpression(classNode)
177
- // }
154
+ let keepDeclaration
155
+
156
+ if (declaration.type === 'FunctionDeclaration' ||
157
+ declaration.type === 'ClassDeclaration') {
158
+ const { id } = declaration
159
+ if (id && splitDefaultNamedDeclarations) {
160
+ // export default function X() {}
161
+ // export default class X {}
162
+ exportValue = identifier(declaration.id.name)
163
+ keepDeclaration = true
164
+ } else {
165
+ // export default function () {}
166
+ // export default class {}
167
+ exportValue = toExpression(declaration)
168
+ }
178
169
  }
179
170
 
180
- if (needExportExpression) {
181
- let exportStat
171
+ let exportStat
182
172
 
183
- if (i + 1 === length && isOnlyDefaultExport) {
184
- exportStat = returnStatement(exportValue)
185
- needReturnExport = false
186
- } else {
187
- exportStat = exportStatement(exportsVar, 'default', exportValue)
188
- }
173
+ if (i + 1 === length && isOnlyDefaultExport) {
174
+ exportStat = returnStatement(exportValue)
175
+ needReturnExport = false
176
+ } else {
177
+ exportStat = exportStatement(exportsVar, 'default', exportValue)
178
+ }
189
179
 
190
- body[i] = exportStat
180
+ // This changes the original code by putting the name of the exported
181
+ // function or class to the module scope. Being able to access the class
182
+ // by name simplifies other AST manipulations, which would have to be more
183
+ // complicated. Or the developer would have to help the manipulator
184
+ // by separating the export expression to a declaration and an export
185
+ // of an identifier. Use it if it doesn't break your code.
186
+ //
187
+ // // original, the name X is not in the module scope
188
+ // export default class X {}
189
+ //
190
+ // // converted, the name X is in the module scope
191
+ // class X {}
192
+ // export default X
193
+ if (keepDeclaration) {
194
+ body.splice(i++, 0, declaration)
195
+ ++length
191
196
  }
197
+ body[i] = exportStat
192
198
  }
193
199
 
194
200
  // export {x as y}
@@ -244,7 +250,7 @@ export function transformEsmToAmd(program, options) {
244
250
  const importVar = generateUidIdentifier(exportSource.value, program)
245
251
  importVars.push(importVar)
246
252
 
247
- for (let specifier of specifiers) {
253
+ for (const specifier of specifiers) {
248
254
  const { exported, local } = specifier
249
255
  const { name } = local
250
256
  let localName
@@ -318,6 +324,17 @@ export function transformEsmToAmd(program, options) {
318
324
  }
319
325
  }
320
326
 
327
+ // no imports and exports, do not wrap to AMD module, if AMD/UMD should be assumed
328
+ if (options.skipIfNoImportExport && !(importPaths.length || hasExport)) {
329
+ options.onAfterTransform?.({
330
+ ...options,
331
+ updated: false,
332
+ program,
333
+ callbackBody: body
334
+ })
335
+ return false
336
+ }
337
+
321
338
  // adding define wrapper
322
339
  if (hasExport && needReturnExport) {
323
340
  let returnStat
@@ -350,10 +367,13 @@ export function transformEsmToAmd(program, options) {
350
367
 
351
368
  options.onAfterTransform?.({
352
369
  ...options,
370
+ updated: true,
353
371
  program,
354
372
  callbackBody: body
355
373
  })
356
374
 
375
+ return true
376
+
357
377
  function addExportStatement({ exported, local }) {
358
378
  const asName = exported.name
359
379
  if (asName !== 'default') {
@@ -1,5 +1,6 @@
1
1
  import { detectDefinesOrRequires, updateAmdDeps, callAmdUpdateHooks } from './amd'
2
2
  import { detectImportsAndExports, transformEsmToAmd } from './esm'
3
+ import { processOrSkipByComment } from './comments'
3
4
 
4
5
  export function transformAst(program, options = {}) {
5
6
  const amds = detectDefinesOrRequires(program)
@@ -14,10 +15,9 @@ export function transformAst(program, options = {}) {
14
15
  result.updated ||= updated
15
16
  }
16
17
  } else {
17
- transformEsmToAmd(program, options)
18
- result.updated = true
18
+ result.updated = transformEsmToAmd(program, options)
19
19
  }
20
20
  return result
21
21
  }
22
22
 
23
- export { detectDefinesOrRequires, detectImportsAndExports }
23
+ export { detectDefinesOrRequires, detectImportsAndExports, processOrSkipByComment }
package/src/write-text.js CHANGED
@@ -5,8 +5,8 @@ let writeText
5
5
  // Initialise the writeText variable with a function to write to a file.
6
6
  /* istanbul ignore if */
7
7
  if (!(typeof window !== 'undefined' && window.navigator && window.document)) {
8
- const { writeFileSync, mkdirSync } = require.nodeRequire ? require.nodeRequire('fs') : require('fs')
9
- const { dirname } = require.nodeRequire ? require.nodeRequire('path') : require('path')
8
+ const { writeFileSync, mkdirSync } = require.nodeRequire ? require.nodeRequire('fs') : require('node:fs')
9
+ const { dirname } = require.nodeRequire ? require.nodeRequire('path') : require('node:path')
10
10
  writeText = (path, content) => {
11
11
  mkdirSync(dirname(path), { recursive: true })
12
12
  writeFileSync(path, content)