@checkdigit/typescript-config 3.3.0-PR.30-dcb8 → 3.3.0-PR.30-f38e
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/bin/builder.mjs +43 -17
- package/package.json +1 -1
- package/src/builder/builder.mts +68 -17
- package/src/builder/builder.spec.mts +108 -3
package/bin/builder.mjs
CHANGED
@@ -39,21 +39,45 @@ function setup(type2) {
|
|
39
39
|
});
|
40
40
|
};
|
41
41
|
}
|
42
|
-
async function builder_default({ type: type2, inDir: inDir2, outDir: outDir2 }) {
|
42
|
+
async function builder_default({ type: type2, entryPoint, inDir: inDir2, outDir: outDir2, outFile }) {
|
43
43
|
const messages2 = [];
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
(file) => file.endsWith(".ts")
|
44
|
+
assert.ok(
|
45
|
+
entryPoint === void 0 && outFile === void 0 || entryPoint !== void 0 && outFile !== void 0,
|
46
|
+
"entryPoint and outFile must both be provided"
|
48
47
|
);
|
49
|
-
const
|
50
|
-
const
|
48
|
+
const allSourceFiles = await getFiles(inDir2);
|
49
|
+
const productionSourceFiles = entryPoint === void 0 ? allSourceFiles.filter((file) => file.endsWith(".ts")) : [path.join(inDir2, entryPoint)];
|
51
50
|
const program = typescript.createProgram(productionSourceFiles, {
|
52
|
-
|
51
|
+
module: typescript.ModuleKind.ESNext,
|
52
|
+
moduleResolution: typescript.ModuleResolutionKind.Bundler,
|
53
|
+
target: typescript.ScriptTarget.ESNext,
|
54
|
+
declaration: true,
|
53
55
|
noEmitOnError: true,
|
54
56
|
emitDeclarationOnly: true,
|
55
57
|
rootDir: inDir2,
|
56
|
-
outDir: outDir2
|
58
|
+
outDir: outDir2,
|
59
|
+
noLib: false,
|
60
|
+
skipLibCheck: true,
|
61
|
+
strict: true,
|
62
|
+
preserveConstEnums: true,
|
63
|
+
noImplicitReturns: true,
|
64
|
+
noUnusedLocals: true,
|
65
|
+
noUnusedParameters: true,
|
66
|
+
alwaysStrict: true,
|
67
|
+
verbatimModuleSyntax: false,
|
68
|
+
noFallthroughCasesInSwitch: true,
|
69
|
+
forceConsistentCasingInFileNames: true,
|
70
|
+
emitDecoratorMetadata: true,
|
71
|
+
experimentalDecorators: true,
|
72
|
+
resolveJsonModule: true,
|
73
|
+
esModuleInterop: true,
|
74
|
+
noUncheckedIndexedAccess: true,
|
75
|
+
noPropertyAccessFromIndexSignature: true,
|
76
|
+
allowUnusedLabels: false,
|
77
|
+
allowUnreachableCode: false,
|
78
|
+
noImplicitOverride: true,
|
79
|
+
useUnknownInCatchVariables: true,
|
80
|
+
exactOptionalPropertyTypes: true
|
57
81
|
});
|
58
82
|
const emitResult = program.emit();
|
59
83
|
const allDiagnostics = typescript.sortAndDeduplicateDiagnostics([
|
@@ -78,16 +102,18 @@ async function builder_default({ type: type2, inDir: inDir2, outDir: outDir2 })
|
|
78
102
|
bundle: true,
|
79
103
|
platform: "node",
|
80
104
|
format: type2 === "module" ? "esm" : "cjs",
|
81
|
-
outdir: outDir2,
|
82
105
|
sourcemap: "inline",
|
83
106
|
sourcesContent: false,
|
84
|
-
|
85
|
-
|
86
|
-
{
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
107
|
+
...outFile === void 0 ? {
|
108
|
+
outdir: outDir2,
|
109
|
+
outExtension: { ".js": type2 === "module" ? ".mjs" : ".cjs" },
|
110
|
+
plugins: [
|
111
|
+
{
|
112
|
+
name: "resolve-ts",
|
113
|
+
setup: setup(type2)
|
114
|
+
}
|
115
|
+
]
|
116
|
+
} : { outfile: path.join(outDir2, outFile) }
|
91
117
|
});
|
92
118
|
messages2.push(...buildResult.errors.map((error) => `esbuild error: ${error.text}`));
|
93
119
|
messages2.push(...buildResult.warnings.map((warning) => `esbuild warning: ${warning.text}`));
|
package/package.json
CHANGED
@@ -1 +1 @@
|
|
1
|
-
{"name":"@checkdigit/typescript-config","version":"3.3.0-PR.30-
|
1
|
+
{"name":"@checkdigit/typescript-config","version":"3.3.0-PR.30-f38e","description":"Check Digit standard Typescript configuration","prettier":"@checkdigit/prettier-config","engines":{"node":">=16"},"bin":{"builder":"./bin/builder.mjs"},"peerDependencies":{"@types/node":">=16","esbuild":"0.17.18","typescript":">=5.0.4 <5.1"},"repository":{"type":"git","url":"git+https://github.com/checkdigit/typescript-config.git"},"author":"Check Digit, LLC","license":"MIT","bugs":{"url":"https://github.com/checkdigit/typescript-config/issues"},"homepage":"https://github.com/checkdigit/typescript-config#readme","scripts":{"preversion":"npm test","postversion":"git push && git push --tags","prepare":"npm run build-builder","lint:fix":"eslint -f unix --ext .ts,.mts src --fix","lint":"eslint -f unix --ext .ts,.mts src","prettier":"prettier --ignore-path .gitignore --list-different .","prettier:fix":"prettier --ignore-path .gitignore --write .","test":"npm run ci:compile && npm run ci:test && npm run ci:lint && npm run ci:style","build-builder":"esbuild src/builder/index.mts --bundle --platform=node --format=esm --external:typescript --external:esbuild --outfile=build-builder/builder.mjs && mkdir -p bin && { echo '#!/usr/bin/env node'; cat build-builder/builder.mjs; } > bin/builder.mjs && chmod +x bin/builder.mjs","build-tsc-cjs":"rimraf build-tsc-cjs && tsc --outDir build-tsc-cjs","build-esbuild-cjs":"rimraf build-esbuild-cjs && esbuild ./src/*.ts ./src/*/*.ts --platform=node --bundle --format=cjs --sourcemap=inline --sources-content=false --outdir=build-esbuild-cjs","build-esbuild-cjs-bundle":"rimraf build-esbuild-cjs-bundle && esbuild src/test/index.test.ts --bundle --platform=node --format=cjs --sourcemap=inline --sources-content=false --outfile=build-esbuild-cjs-bundle/test/index.test.cjs","build-esbuild-esm":"rimraf build-esbuild-esm && node bin/builder.mjs --outDir=build-esbuild-esm","build-esbuild-esm-bundle":"rimraf build-esbuild-esm-bundle && esbuild src/test/index.test.ts --bundle --platform=node --format=esm --sourcemap=inline --sources-content=false --outfile=build-esbuild-esm-bundle/test/index.test.mjs","test-jest-esm":"NODE_OPTIONS=\"--experimental-vm-modules\" jest --coverage=false src/*.mts src/*/*.mts src/*/*/*.mts","test-jest-cjs":"jest --coverage=false src/*.ts src/*/*.ts src/*/*/*.ts","test-tsc-cjs":"node --test build-tsc-cjs/test/index.test.js","test-esbuild-cjs":"node --test build-esbuild-cjs/test/index.test.js","test-esbuild-cjs-bundle":"node --test build-esbuild-cjs-bundle/test/index.test.cjs","test-esbuild-esm":"node --test build-esbuild-esm/test/index.test.mjs","test-esbuild-esm-bundle":"echo 'node -test build-esbuild-esm-bundle/test/index.test.mjs'","ci:test":"npm run test-jest-cjs && npm run test-jest-esm &&npm run test-tsc-cjs && npm run test-esbuild-cjs && npm run test-esbuild-cjs-bundle && npm run test-esbuild-esm && npm run test-esbuild-esm-bundle","ci:compile":"npm run build-builder && npm run build-tsc-cjs && npm run build-esbuild-cjs && npm run build-esbuild-cjs-bundle && npm run build-esbuild-esm && npm run build-esbuild-esm-bundle","ci:lint":"npm run lint","ci:style":"npm run prettier"},"devDependencies":{"@checkdigit/prettier-config":"^3.4.0","@types/debug":"^4.1.7","@types/jest":"^29.5.1","@types/uuid":"^9.0.1","@typescript-eslint/eslint-plugin":"^5.59.6","@typescript-eslint/parser":"^5.59.6","debug":"^4.3.4","eslint":"^8.40.0","eslint-config-prettier":"^8.8.0","get-port":"^6.1.2","got":"^11.8.6","jest":"^29.5.0","rimraf":"^5.0.0","ts-jest":"^29.1.0","uuid":"^9.0.0"},"eslintConfig":{"parser":"@typescript-eslint/parser","plugins":["@typescript-eslint"],"parserOptions":{"project":"./tsconfig.json"},"extends":["eslint:all","plugin:@typescript-eslint/recommended","plugin:@typescript-eslint/recommended-requiring-type-checking","plugin:@typescript-eslint/strict","prettier"],"rules":{"@typescript-eslint/non-nullable-type-assertion-style":"error","capitalized-comments":"off","one-var":"off","sort-keys":"off","sort-imports":"off","func-style":["error","declaration",{"allowArrowFunctions":true}],"no-magic-numbers":["error",{"ignore":[0,1,2]}],"no-undefined":"off","no-ternary":"off"},"overrides":[{"files":["*.spec.mts","*.test.mts","*.spec.ts","*.test.ts"],"rules":{"@typescript-eslint/non-nullable-type-assertion-style":"off","@typescript-eslint/ban-types":"off","@typescript-eslint/require-await":"off","@typescript-eslint/consistent-type-definitions":"off","@typescript-eslint/ban-ts-comment":"off","@typescript-eslint/no-unnecessary-condition":"off","@typescript-eslint/consistent-indexed-object-style":"off","@typescript-eslint/no-unused-vars":"off","@typescript-eslint/no-unsafe-member-access":"off","line-comment-position":"off","no-inline-comments":"off","no-param-reassign":"off","id-length":"off","no-magic-numbers":"off","func-names":"off","no-duplicate-imports":"off","symbol-description":"off","no-invalid-this":"off","max-lines-per-function":"off","max-lines":"off","max-statements":"off","no-await-in-loop":"off"}}]},"jest":{"moduleFileExtensions":["js","mjs","cjs","ts","mts","json","node"],"extensionsToTreatAsEsm":[".mts"],"transform":{"^.+\\.mts$":["ts-jest",{"isolatedModules":true,"diagnostics":false,"useESM":true}],"^.+\\.ts$":["ts-jest",{"isolatedModules":true,"diagnostics":false,"useESM":false}]},"collectCoverageFrom":["<rootDir>/src/**","!<rootDir>/src/**/*.spec.mts","!<rootDir>/src/**/*.test.mts","!<rootDir>/src/**/*.spec.ts","!<rootDir>/src/**/*.test.ts"],"testMatch":["<rootDir>/src/**/*.spec.ts","<rootDir>/src/**/*.spec.mts"]},"files":["tsconfig.json","SECURITY.md","/src/"]}
|
package/src/builder/builder.mts
CHANGED
@@ -8,9 +8,31 @@ import typescript from 'typescript';
|
|
8
8
|
import { PluginBuild, build } from 'esbuild';
|
9
9
|
|
10
10
|
export interface BuilderOptions {
|
11
|
+
/**
|
12
|
+
* whether to produce ESM or CommonJS code
|
13
|
+
*/
|
11
14
|
type: 'module' | 'commonjs';
|
15
|
+
|
16
|
+
/**
|
17
|
+
* the entry point for the bundle, relative to the inDir. if not provided, the files in the inDir will be processed
|
18
|
+
* as individual unbundled files
|
19
|
+
*/
|
20
|
+
entryPoint?: string;
|
21
|
+
|
22
|
+
/**
|
23
|
+
* source code
|
24
|
+
*/
|
12
25
|
inDir: string;
|
26
|
+
|
27
|
+
/**
|
28
|
+
* build directory
|
29
|
+
*/
|
13
30
|
outDir: string;
|
31
|
+
|
32
|
+
/**
|
33
|
+
* build file, relative to the outDir
|
34
|
+
*/
|
35
|
+
outFile?: string;
|
14
36
|
}
|
15
37
|
|
16
38
|
/**
|
@@ -51,27 +73,52 @@ function setup(type: 'module' | 'commonjs') {
|
|
51
73
|
}
|
52
74
|
|
53
75
|
// eslint-disable-next-line func-names,max-lines-per-function,max-statements
|
54
|
-
export default async function ({ type, inDir, outDir }: BuilderOptions): Promise<string[]> {
|
76
|
+
export default async function ({ type, entryPoint, inDir, outDir, outFile }: BuilderOptions): Promise<string[]> {
|
55
77
|
const messages: string[] = [];
|
56
78
|
|
79
|
+
assert.ok(
|
80
|
+
(entryPoint === undefined && outFile === undefined) || (entryPoint !== undefined && outFile !== undefined),
|
81
|
+
'entryPoint and outFile must both be provided'
|
82
|
+
);
|
83
|
+
|
57
84
|
/**
|
58
85
|
* Emit declarations using typescript compiler
|
59
86
|
*/
|
60
87
|
const allSourceFiles = await getFiles(inDir);
|
61
|
-
const productionSourceFiles =
|
62
|
-
|
63
|
-
(file) => file.endsWith('.ts')
|
64
|
-
);
|
65
|
-
|
66
|
-
const configFile = typescript.readConfigFile('./tsconfig.json', (name) => typescript.sys.readFile(name));
|
67
|
-
const compilerOptions = typescript.parseJsonConfigFileContent(configFile.config, typescript.sys, './');
|
88
|
+
const productionSourceFiles =
|
89
|
+
entryPoint === undefined ? allSourceFiles.filter((file) => file.endsWith('.ts')) : [path.join(inDir, entryPoint)];
|
68
90
|
|
69
91
|
const program = typescript.createProgram(productionSourceFiles, {
|
70
|
-
|
92
|
+
module: typescript.ModuleKind.ESNext,
|
93
|
+
moduleResolution: typescript.ModuleResolutionKind.Bundler,
|
94
|
+
target: typescript.ScriptTarget.ESNext,
|
95
|
+
declaration: true,
|
71
96
|
noEmitOnError: true,
|
72
97
|
emitDeclarationOnly: true,
|
73
98
|
rootDir: inDir,
|
74
99
|
outDir,
|
100
|
+
noLib: false,
|
101
|
+
skipLibCheck: true,
|
102
|
+
strict: true,
|
103
|
+
preserveConstEnums: true,
|
104
|
+
noImplicitReturns: true,
|
105
|
+
noUnusedLocals: true,
|
106
|
+
noUnusedParameters: true,
|
107
|
+
alwaysStrict: true,
|
108
|
+
verbatimModuleSyntax: false,
|
109
|
+
noFallthroughCasesInSwitch: true,
|
110
|
+
forceConsistentCasingInFileNames: true,
|
111
|
+
emitDecoratorMetadata: true,
|
112
|
+
experimentalDecorators: true,
|
113
|
+
resolveJsonModule: true,
|
114
|
+
esModuleInterop: true,
|
115
|
+
noUncheckedIndexedAccess: true,
|
116
|
+
noPropertyAccessFromIndexSignature: true,
|
117
|
+
allowUnusedLabels: false,
|
118
|
+
allowUnreachableCode: false,
|
119
|
+
noImplicitOverride: true,
|
120
|
+
useUnknownInCatchVariables: true,
|
121
|
+
exactOptionalPropertyTypes: true,
|
75
122
|
});
|
76
123
|
const emitResult = program.emit();
|
77
124
|
const allDiagnostics = typescript.sortAndDeduplicateDiagnostics([
|
@@ -101,16 +148,20 @@ export default async function ({ type, inDir, outDir }: BuilderOptions): Promise
|
|
101
148
|
bundle: true,
|
102
149
|
platform: 'node',
|
103
150
|
format: type === 'module' ? 'esm' : 'cjs',
|
104
|
-
outdir: outDir,
|
105
151
|
sourcemap: 'inline',
|
106
152
|
sourcesContent: false,
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
153
|
+
...(outFile === undefined
|
154
|
+
? {
|
155
|
+
outdir: outDir,
|
156
|
+
outExtension: { '.js': type === 'module' ? '.mjs' : '.cjs' },
|
157
|
+
plugins: [
|
158
|
+
{
|
159
|
+
name: 'resolve-ts',
|
160
|
+
setup: setup(type),
|
161
|
+
},
|
162
|
+
],
|
163
|
+
}
|
164
|
+
: { outfile: path.join(outDir, outFile) }),
|
114
165
|
});
|
115
166
|
|
116
167
|
messages.push(...buildResult.errors.map((error) => `esbuild error: ${error.text}`));
|
@@ -26,9 +26,59 @@ const exportDefaultFunctionModule = {
|
|
26
26
|
[`index.ts`]: `export default function () { return 'hello world' }\n`,
|
27
27
|
};
|
28
28
|
|
29
|
-
|
30
|
-
|
31
|
-
|
29
|
+
const importExternalModule = {
|
30
|
+
[`index.ts`]: `
|
31
|
+
import { hello as test } from 'test-esm-module';
|
32
|
+
import util from 'node:util';
|
33
|
+
export const hello = { test, message: util.format('hello %s', 'world') };
|
34
|
+
`,
|
35
|
+
};
|
36
|
+
|
37
|
+
const testNodeModules = {
|
38
|
+
[`test-cjs-module`]: {
|
39
|
+
source: {
|
40
|
+
[`index.js`]: `module.exports.goodbye = 'world';`,
|
41
|
+
[`index.d.ts`]: `export declare const goodbye = "world";\n`,
|
42
|
+
},
|
43
|
+
},
|
44
|
+
[`test-esm-module`]: {
|
45
|
+
type: 'module',
|
46
|
+
source: {
|
47
|
+
[`index.js`]: `export const hello = 'world';`,
|
48
|
+
[`index.d.ts`]: `export declare const hello = "world";\n`,
|
49
|
+
},
|
50
|
+
},
|
51
|
+
} as const;
|
52
|
+
|
53
|
+
interface NodeModule {
|
54
|
+
[name: string]: {
|
55
|
+
type?: 'module' | 'commonjs';
|
56
|
+
source: {
|
57
|
+
[file: string]: string;
|
58
|
+
};
|
59
|
+
};
|
60
|
+
}
|
61
|
+
|
62
|
+
async function writeNodeModules(directory: string, nodeModules: NodeModule) {
|
63
|
+
const nodeModulesDirectory = path.join(directory, 'node_modules');
|
64
|
+
for (const [name, nodeModule] of Object.entries(nodeModules)) {
|
65
|
+
const nodeModuleDirectory = path.join(nodeModulesDirectory, name);
|
66
|
+
await fs.mkdir(nodeModuleDirectory, { recursive: true });
|
67
|
+
await fs.writeFile(
|
68
|
+
path.join(nodeModuleDirectory, 'package.json'),
|
69
|
+
JSON.stringify({
|
70
|
+
type: nodeModule.type ?? 'commonjs',
|
71
|
+
})
|
72
|
+
);
|
73
|
+
for (const [file, content] of Object.entries(nodeModule.source)) {
|
74
|
+
await fs.writeFile(path.join(nodeModuleDirectory, file), content);
|
75
|
+
}
|
76
|
+
}
|
77
|
+
}
|
78
|
+
|
79
|
+
async function write(directory: string, files: Record<string, string>): Promise<void> {
|
80
|
+
await fs.mkdir(directory, { recursive: true });
|
81
|
+
await Promise.all(Object.entries(files).map(([name, content]) => fs.writeFile(path.join(directory, name), content)));
|
32
82
|
}
|
33
83
|
|
34
84
|
async function read(dir: string): Promise<Record<string, string>> {
|
@@ -305,4 +355,59 @@ describe('test builder', () => {
|
|
305
355
|
const output2 = await import(path.join(outDir, 'index.cjs'));
|
306
356
|
assert.equal(output2.default.default, 'worldworld');
|
307
357
|
});
|
358
|
+
|
359
|
+
it('should bundle an ESM module that imports a second ESM module', async () => {
|
360
|
+
const id = uuid();
|
361
|
+
const inDir = path.join(os.tmpdir(), `in-dir-${id}`, 'src');
|
362
|
+
const outDir = path.join(os.tmpdir(), `out-dir-${id}`, 'build');
|
363
|
+
await write(inDir, twoModules);
|
364
|
+
assert.deepEqual(
|
365
|
+
await builder({ type: 'module', entryPoint: 'index.ts', outFile: 'index.mjs', inDir, outDir }),
|
366
|
+
[]
|
367
|
+
);
|
368
|
+
assert.deepEqual(await read(outDir), {
|
369
|
+
'index.d.ts': 'declare const _default: string;\nexport default _default;\n',
|
370
|
+
'index.mjs':
|
371
|
+
'var hello = "world";\n' +
|
372
|
+
'\n' +
|
373
|
+
'var src_default = hello + "world";\n' +
|
374
|
+
'export {\n' +
|
375
|
+
' src_default as default\n' +
|
376
|
+
'};\n',
|
377
|
+
'thing.d.ts': 'export declare const hello = "world";\n',
|
378
|
+
});
|
379
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
380
|
+
const output = await import(path.join(outDir, 'index.mjs'));
|
381
|
+
assert.equal(output.default, 'worldworld');
|
382
|
+
});
|
383
|
+
|
384
|
+
it('should bundle an ESM module that imports external modules', async () => {
|
385
|
+
const id = uuid();
|
386
|
+
const moduleDir = path.join(os.tmpdir(), `in-dir-${id}`);
|
387
|
+
const inDir = path.join(moduleDir, 'src');
|
388
|
+
const outDir = path.join(os.tmpdir(), `out-dir-${id}`, 'build');
|
389
|
+
await write(inDir, importExternalModule);
|
390
|
+
await writeNodeModules(moduleDir, testNodeModules);
|
391
|
+
assert.deepEqual(
|
392
|
+
await builder({ type: 'module', entryPoint: 'index.ts', outFile: 'index.mjs', inDir, outDir }),
|
393
|
+
[]
|
394
|
+
);
|
395
|
+
assert.deepEqual(await read(outDir), {
|
396
|
+
'index.d.ts': 'export declare const hello: {\n test: string;\n message: string;\n};\n',
|
397
|
+
'index.mjs':
|
398
|
+
'var hello = "world";\n' +
|
399
|
+
'\n' +
|
400
|
+
'import util from "node:util";\n' +
|
401
|
+
'var hello2 = { test: hello, message: util.format("hello %s", "world") };\n' +
|
402
|
+
'export {\n' +
|
403
|
+
' hello2 as hello\n' +
|
404
|
+
'};\n',
|
405
|
+
});
|
406
|
+
// eslint-disable-next-line @typescript-eslint/no-unsafe-assignment
|
407
|
+
const output = await import(path.join(outDir, 'index.mjs'));
|
408
|
+
assert.deepEqual(output.hello, {
|
409
|
+
message: 'hello world',
|
410
|
+
test: 'world',
|
411
|
+
});
|
412
|
+
});
|
308
413
|
});
|