Skip to content

Commit 7d38ab1

Browse files
committed
add esm
1 parent bd3ee52 commit 7d38ab1

File tree

4 files changed

+126
-2
lines changed

4 files changed

+126
-2
lines changed

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ build
77
dist
88
docs-dist
99
lib
10+
esm
1011
.idea
1112
.docz
1213
.tscache

package.json

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,15 @@
11
{
22
"name": "graphql-js-tree",
3-
"version": "3.0.3",
3+
"version": "3.0.4",
44
"private": false,
55
"license": "MIT",
66
"description": "GraphQL Parser providing simplier structure",
77
"homepage": "https://graphqleditor.com",
88
"main": "lib/index.js",
9+
"module": "esm/index.mjs",
910
"types": "lib/index.d.ts",
1011
"scripts": {
11-
"build": "tspc --build tsconfig.build.json",
12+
"build": "tspc --build tsconfig.build.json && tspc --build tsconfig.build.esm.json && node scripts/fix-esm-extensions.js",
1213
"start": "tspc --build tsconfig.build.json --watch",
1314
"test": "jest",
1415
"lint": "tspc && eslint \"./src/**/*.{ts,js}\" --quiet --fix"
@@ -41,5 +42,12 @@
4142
},
4243
"peerDependencies": {
4344
"graphql": "^16.0.0 || ^17.0.0"
45+
},
46+
"exports": {
47+
".": {
48+
"types": "./lib/index.d.ts",
49+
"require": "./lib/index.js",
50+
"import": "./esm/index.mjs"
51+
}
4452
}
4553
}

scripts/fix-esm-extensions.js

Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
// Post-build fixer for strict Node ESM support.
2+
// - Renames esm files from .js to .mjs
3+
// - Rewrites relative import/export specifiers to include explicit extensions
4+
// and use .mjs (including directory index resolution)
5+
const fs = require('fs');
6+
const path = require('path');
7+
8+
const esmRoot = path.resolve(__dirname, '..', 'esm');
9+
10+
/** @param {string} p */
11+
function isDir(p) {
12+
try {
13+
return fs.statSync(p).isDirectory();
14+
} catch {
15+
return false;
16+
}
17+
}
18+
19+
/** @param {string} dir */
20+
function walk(dir) {
21+
const entries = fs.readdirSync(dir, { withFileTypes: true });
22+
/** @type {string[]} */
23+
const files = [];
24+
for (const e of entries) {
25+
const full = path.join(dir, e.name);
26+
if (e.isDirectory()) files.push(...walk(full));
27+
else files.push(full);
28+
}
29+
return files;
30+
}
31+
32+
function ensureRelativeWithExt(spec, fromFile) {
33+
if (!spec.startsWith('./') && !spec.startsWith('../')) return spec; // external
34+
// Normalize and resolve target on disk relative to fromFile
35+
const fromDir = path.dirname(fromFile);
36+
const raw = path.resolve(fromDir, spec);
37+
38+
// If spec already has an extension, normalize to .mjs when it points into esm
39+
if (/\.(js|mjs|cjs)$/.test(spec)) {
40+
// Convert .js to .mjs for intra-esm imports
41+
return spec.replace(/\.js$/i, '.mjs');
42+
}
43+
44+
// Try file.mjs
45+
if (fs.existsSync(raw + '.mjs')) {
46+
return spec + '.mjs';
47+
}
48+
// Try file.js (before rename step) or when resolving precomputed graph
49+
if (fs.existsSync(raw + '.js')) {
50+
return spec + '.mjs';
51+
}
52+
// Try directory index
53+
if (isDir(raw)) {
54+
if (fs.existsSync(path.join(raw, 'index.mjs'))) {
55+
return spec.replace(/\/?$/, '/') + 'index.mjs';
56+
}
57+
if (fs.existsSync(path.join(raw, 'index.js'))) {
58+
return spec.replace(/\/?$/, '/') + 'index.mjs';
59+
}
60+
}
61+
// Fallback: append .mjs
62+
return spec + '.mjs';
63+
}
64+
65+
function rewriteFile(file) {
66+
let code = fs.readFileSync(file, 'utf8');
67+
// import ... from '...'; export ... from '...'; dynamic import('...')
68+
code = code.replace(/(import\s+[^'"()]+?from\s*["'])([^"']+)(["'])/g, (m, a, s, b) => a + ensureRelativeWithExt(s, file) + b);
69+
code = code.replace(/(export\s+[^'"()]+?from\s*["'])([^"']+)(["'])/g, (m, a, s, b) => a + ensureRelativeWithExt(s, file) + b);
70+
code = code.replace(/(import\s*\(\s*["'])([^"']+)(["']\s*\))/g, (m, a, s, b) => a + ensureRelativeWithExt(s, file) + b);
71+
fs.writeFileSync(file, code);
72+
}
73+
74+
function main() {
75+
if (!fs.existsSync(esmRoot)) return;
76+
// First rename all .js files to .mjs to ensure ESM mode regardless of package type
77+
const files = walk(esmRoot).filter((f) => f.endsWith('.js'));
78+
// Rename leaf files first to avoid conflicts
79+
for (const f of files.sort((a, b) => b.length - a.length)) {
80+
fs.renameSync(f, f.slice(0, -3) + '.mjs');
81+
}
82+
// Now rewrite imports in all .mjs files
83+
const mjsFiles = walk(esmRoot).filter((f) => f.endsWith('.mjs'));
84+
for (const f of mjsFiles) rewriteFile(f);
85+
}
86+
87+
main();

tsconfig.build.esm.json

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
{
2+
"compilerOptions": {
3+
"target": "es6",
4+
"module": "esnext",
5+
"moduleResolution": "node",
6+
"experimentalDecorators": true,
7+
"declaration": false,
8+
"removeComments": true,
9+
"noUnusedLocals": true,
10+
"strictNullChecks": true,
11+
"skipLibCheck": true,
12+
"strict": true,
13+
"outDir": "./esm",
14+
"lib": ["es6", "es7", "esnext", "dom"],
15+
"rootDir": "./src",
16+
"baseUrl": "./src/",
17+
"paths": {
18+
"@/*": ["./*"]
19+
},
20+
"plugins": [
21+
{
22+
"transform": "typescript-transform-paths"
23+
}
24+
]
25+
},
26+
"exclude": ["lib", "esm", "node_modules", "docs", "**/__tests__", "generated", "examples", "**/*.spec.ts"]
27+
}
28+

0 commit comments

Comments
 (0)