-
Notifications
You must be signed in to change notification settings - Fork 670
/
Copy pathfix-esm-imports.mjs
79 lines (68 loc) · 3.09 KB
/
fix-esm-imports.mjs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
// Author: Ron B. Yeh
// node ./tools/fix-esm-imports.mjs ./build/esm/
// See the `build:esm` task in Gruntfile.js.
// Used by Gruntfile.js to add .js extensions to all imports and exports under the `vexflow/build/esm/` directory.
// ES module import statements do not work without the .js extension.
// This script recursively walks `build/esm/` and fixes imports & exports in every JS file.
// It adds a .js extension to every import / export of a file, fixing files in place. For example:
// import { Fraction } from './fraction'; => import { Fraction } from './fraction.js';
// export * from './barnote'; => export * from './barnote.js';
// The ESM files are produced by the tsc compiler, which does NOT add .js extensions to imports / exports.
// When importing the default files via a web page or node, you get errors like:
// vexflow.js:1 GET https://..... net::ERR_ABORTED 404 (Not Found)
// Error [ERR_MODULE_NOT_FOUND]: Cannot find module...
// The fix is to add the .js file extensions, so that the browser and node can resolve the imports properly.
// The TypeScript team has said they do not plan to add this feature.
// See: https://github.com/microsoft/TypeScript/issues/16577#issuecomment-754941937
// Limitations:
// - Assume single quoted strings.
// - Assume import(...) function uses only string literals.
import * as fs from 'fs';
import * as path from 'path';
const startingDirectory = process.argv[2] ?? __dirname;
function* walk(dir) {
const files = fs.readdirSync(dir, { withFileTypes: true });
for (const file of files) {
if (file.isDirectory()) {
yield* walk(path.join(dir, file.name));
} else {
yield path.join(dir, file.name);
}
}
}
// Read and write files in place.
function fixImportsAndExports(filePath) {
const contents = fs.readFileSync(filePath, 'utf8');
// Line by line regex replace!
const lines = contents.split('\n');
const newLines = lines.map((line, index) => {
if (line.startsWith('import ')) {
if (line.endsWith(`.js';`)) {
return line;
}
const fixedLine = line
.replace(/^import (.*?)\.';$/gm, "import $1./index';") // Ends with dot: import '.'; => import './index';
.replace(/^import (.*?)\/';$/gm, "import $1/index';") // Ends with slash: import './src/'; => import './src/index';
.replace(/^import (.*?)';$/gm, "import $1.js';"); // Normal case: import './file'; => import './file.js';
return fixedLine;
} else if (line.startsWith('export ') && line.includes(' from ')) {
if (line.endsWith(`.js';`)) {
return line;
}
const fixedLine = line
.replace(/^export (.*?) from (.*?)\.';$/gm, "export $1 from $2./index';")
.replace(/^export (.*?) from (.*?)\/';$/gm, "export $1 from $2/index';")
.replace(/^export (.*?) from (.*?)';$/gm, "export $1 from $2.js';");
return fixedLine;
} else {
return line;
}
});
const newContents = newLines.join('\n');
fs.writeFileSync(filePath, newContents);
}
for (const filePath of walk(startingDirectory)) {
if (filePath.endsWith('.js')) {
fixImportsAndExports(filePath);
}
}