Skip to content

Commit f138246

Browse files
committed
try imports
1 parent 8b86cd2 commit f138246

13 files changed

+235
-128
lines changed

integrationTests/ts/package.json

-6
Original file line numberDiff line numberDiff line change
@@ -8,12 +8,6 @@
88
"dependencies": {
99
"graphql": "file:../graphql.tgz",
1010
"graphql-esm": "file:../graphql-esm.tgz",
11-
"typescript-4.4": "npm:typescript@4.4.x",
12-
"typescript-4.5": "npm:typescript@4.5.x",
13-
"typescript-4.6": "npm:typescript@4.6.x",
14-
"typescript-4.7": "npm:typescript@4.7.x",
15-
"typescript-4.8": "npm:typescript@4.8.x",
16-
"typescript-4.9": "npm:typescript@4.9.x",
1711
"typescript-5.0": "npm:typescript@5.0.x",
1812
"typescript-5.1": "npm:typescript@5.1.x",
1913
"typescript-5.2": "npm:typescript@5.2.x",

package.json

+6
Original file line numberDiff line numberDiff line change
@@ -28,6 +28,12 @@
2828
"engines": {
2929
"node": "^16.19.0 || ^18.14.0 || >=19.7.0"
3030
},
31+
"imports": {
32+
"#instanceOf": {
33+
"development": "./src/jsutils/instanceOfForDevelopment.ts",
34+
"default": "./src/jsutils/instanceOf.ts"
35+
}
36+
},
3137
"scripts": {
3238
"preversion": "bash -c '. ./resources/checkgit.sh && npm ci --ignore-scripts'",
3339
"version": "node --loader ts-node/esm resources/gen-version.ts && npm test && git add src/version.ts",

resources/build-deno.ts

+42-1
Original file line numberDiff line numberDiff line change
@@ -5,8 +5,10 @@ import ts from 'typescript';
55

66
import { changeExtensionInImportPaths } from './change-extension-in-import-paths.js';
77
import { inlineInvariant } from './inline-invariant.js';
8+
import type { ImportsMap } from './utils.js';
89
import {
910
prettify,
11+
readPackageJSON,
1012
readTSConfig,
1113
showDirStats,
1214
writeGeneratedFile,
@@ -15,7 +17,10 @@ import {
1517
fs.rmSync('./denoDist', { recursive: true, force: true });
1618
fs.mkdirSync('./denoDist');
1719

18-
const tsProgram = ts.createProgram(['src/index.ts'], readTSConfig());
20+
const tsProgram = ts.createProgram(
21+
['src/index.ts', 'src/jsutils/instanceOf.ts'],
22+
readTSConfig(),
23+
);
1924
for (const sourceFile of tsProgram.getSourceFiles()) {
2025
if (
2126
tsProgram.isSourceFileFromExternalLibrary(sourceFile) ||
@@ -45,4 +50,40 @@ for (const sourceFile of tsProgram.getSourceFiles()) {
4550
fs.copyFileSync('./LICENSE', './denoDist/LICENSE');
4651
fs.copyFileSync('./README.md', './denoDist/README.md');
4752

53+
const imports = getImports();
54+
const importsJsonPath = `./denoDist/imports.json`;
55+
const prettified = await prettify(importsJsonPath, JSON.stringify(imports));
56+
writeGeneratedFile(importsJsonPath, prettified);
57+
4858
showDirStats('./denoDist');
59+
60+
function getImports(): ImportsMap {
61+
const packageJSON = readPackageJSON();
62+
const newImports: ImportsMap = {};
63+
for (const [key, value] of Object.entries(packageJSON.imports)) {
64+
if (typeof value === 'string') {
65+
newImports[key] = updateImportPath(value, '.ts');
66+
continue;
67+
}
68+
const findCondition = findDefault(value);
69+
if (findCondition !== undefined) {
70+
newImports[key] = updateImportPath(findCondition, '.ts');
71+
}
72+
}
73+
return newImports;
74+
}
75+
76+
function updateImportPath(value: string, extension: string) {
77+
return value.replace(/\/src\//g, '/').replace(/\.ts$/, extension);
78+
}
79+
80+
function findDefault(importsMap: ImportsMap): string | undefined {
81+
for (const [key, value] of Object.entries(importsMap)) {
82+
if (key === 'deno' || key === 'default') {
83+
if (typeof value === 'string') {
84+
return value;
85+
}
86+
return findDefault(value);
87+
}
88+
}
89+
}

resources/build-npm.ts

+41-2
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import ts from 'typescript';
66

77
import { changeExtensionInImportPaths } from './change-extension-in-import-paths.js';
88
import { inlineInvariant } from './inline-invariant.js';
9+
import type { ImportsMap } from './utils.js';
910
import {
1011
prettify,
1112
readPackageJSON,
@@ -102,12 +103,23 @@ async function buildPackage(outDir: string, isESMOnly: boolean): Promise<void> {
102103
packageJSON.exports['./*.js'] = './*.js';
103104
packageJSON.exports['./*'] = './*.js';
104105

106+
packageJSON.imports = mapImports(packageJSON.imports, (value: string) =>
107+
updateImportPath(value, '.js'),
108+
);
109+
110+
packageJSON.type = 'module';
105111
packageJSON.publishConfig.tag += '-esm';
106112
packageJSON.version += '+esm';
107113
} else {
108-
delete packageJSON.type;
114+
packageJSON.type = 'commonjs';
109115
packageJSON.main = 'index';
110116
packageJSON.module = 'index.mjs';
117+
118+
packageJSON.imports = mapImports(packageJSON.imports, (value: string) => ({
119+
import: updateImportPath(value, '.mjs'),
120+
default: updateImportPath(value, '.js'),
121+
}));
122+
111123
emitTSFiles({ outDir, module: 'commonjs', extension: '.js' });
112124
emitTSFiles({ outDir, module: 'es2020', extension: '.mjs' });
113125
}
@@ -121,6 +133,25 @@ async function buildPackage(outDir: string, isESMOnly: boolean): Promise<void> {
121133
writeGeneratedFile(packageJsonPath, prettified);
122134
}
123135

136+
function updateImportPath(value: string, extension: string) {
137+
return value.replace(/\/src\//g, '/').replace(/\.ts$/, extension);
138+
}
139+
140+
function mapImports(
141+
imports: ImportsMap,
142+
replacer: (value: string) => string | ImportsMap,
143+
): ImportsMap {
144+
const newImports: ImportsMap = {};
145+
for (const [key, value] of Object.entries(imports)) {
146+
if (typeof value === 'string') {
147+
newImports[key] = replacer(value);
148+
continue;
149+
}
150+
newImports[key] = mapImports(value, replacer);
151+
}
152+
return newImports;
153+
}
154+
124155
// Based on https://github.com/Microsoft/TypeScript/wiki/Using-the-Compiler-API#getting-the-dts-from-a-javascript-file
125156
function emitTSFiles(options: {
126157
outDir: string;
@@ -143,7 +174,15 @@ function emitTSFiles(options: {
143174
tsHost.writeFile = (filepath, body) =>
144175
writeGeneratedFile(filepath.replace(/.js$/, extension), body);
145176

146-
const tsProgram = ts.createProgram(['src/index.ts'], tsOptions, tsHost);
177+
const tsProgram = ts.createProgram(
178+
[
179+
'src/index.ts',
180+
'src/jsutils/instanceOf.ts',
181+
'src/jsutils/instanceOfForDevelopment.ts',
182+
],
183+
tsOptions,
184+
tsHost,
185+
);
147186
const tsResult = tsProgram.emit(undefined, undefined, undefined, undefined, {
148187
after: [changeExtensionInImportPaths({ extension }), inlineInvariant],
149188
});

resources/utils.ts

+5
Original file line numberDiff line numberDiff line change
@@ -227,6 +227,10 @@ export function writeGeneratedFile(filepath: string, body: string): void {
227227
fs.writeFileSync(filepath, body);
228228
}
229229

230+
export interface ImportsMap {
231+
[path: string]: string | ImportsMap;
232+
}
233+
230234
interface PackageJSON {
231235
description: string;
232236
version: string;
@@ -235,6 +239,7 @@ interface PackageJSON {
235239
scripts?: { [name: string]: string };
236240
type?: string;
237241
exports: { [path: string]: string };
242+
imports: ImportsMap;
238243
types?: string;
239244
typesVersions: { [ranges: string]: { [path: string]: Array<string> } };
240245
devDependencies?: { [name: string]: string };

src/jsutils/__tests__/instanceOf-test.ts

+1-61
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@ describe('instanceOf', () => {
77
it('do not throw on values without prototype', () => {
88
class Foo {
99
get [Symbol.toStringTag]() {
10+
/* c8 ignore next 2 */
1011
return 'Foo';
1112
}
1213
}
@@ -15,65 +16,4 @@ describe('instanceOf', () => {
1516
expect(instanceOf(null, Foo)).to.equal(false);
1617
expect(instanceOf(Object.create(null), Foo)).to.equal(false);
1718
});
18-
19-
it('detect name clashes with older versions of this lib', () => {
20-
function oldVersion() {
21-
class Foo {}
22-
return Foo;
23-
}
24-
25-
function newVersion() {
26-
class Foo {
27-
get [Symbol.toStringTag]() {
28-
return 'Foo';
29-
}
30-
}
31-
return Foo;
32-
}
33-
34-
const NewClass = newVersion();
35-
const OldClass = oldVersion();
36-
expect(instanceOf(new NewClass(), NewClass)).to.equal(true);
37-
expect(() => instanceOf(new OldClass(), NewClass)).to.throw();
38-
});
39-
40-
it('allows instances to have share the same constructor name', () => {
41-
function getMinifiedClass(tag: string) {
42-
class SomeNameAfterMinification {
43-
get [Symbol.toStringTag]() {
44-
return tag;
45-
}
46-
}
47-
return SomeNameAfterMinification;
48-
}
49-
50-
const Foo = getMinifiedClass('Foo');
51-
const Bar = getMinifiedClass('Bar');
52-
expect(instanceOf(new Foo(), Bar)).to.equal(false);
53-
expect(instanceOf(new Bar(), Foo)).to.equal(false);
54-
55-
const DuplicateOfFoo = getMinifiedClass('Foo');
56-
expect(() => instanceOf(new DuplicateOfFoo(), Foo)).to.throw();
57-
expect(() => instanceOf(new Foo(), DuplicateOfFoo)).to.throw();
58-
});
59-
60-
it('fails with descriptive error message', () => {
61-
function getFoo() {
62-
class Foo {
63-
get [Symbol.toStringTag]() {
64-
return 'Foo';
65-
}
66-
}
67-
return Foo;
68-
}
69-
const Foo1 = getFoo();
70-
const Foo2 = getFoo();
71-
72-
expect(() => instanceOf(new Foo1(), Foo2)).to.throw(
73-
/^Cannot use Foo "{}" from another module or realm./m,
74-
);
75-
expect(() => instanceOf(new Foo2(), Foo1)).to.throw(
76-
/^Cannot use Foo "{}" from another module or realm./m,
77-
);
78-
});
7919
});
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,83 @@
1+
import { expect } from 'chai';
2+
import { describe, it } from 'mocha';
3+
4+
import { instanceOf as instanceOfForDevelopment } from '../instanceOfForDevelopment.js';
5+
6+
describe('instanceOfForDevelopment', () => {
7+
it('do not throw on values without prototype', () => {
8+
class Foo {
9+
get [Symbol.toStringTag]() {
10+
return 'Foo';
11+
}
12+
}
13+
14+
expect(instanceOfForDevelopment(true, Foo)).to.equal(false);
15+
expect(instanceOfForDevelopment(null, Foo)).to.equal(false);
16+
expect(instanceOfForDevelopment(Object.create(null), Foo)).to.equal(false);
17+
});
18+
19+
it('detect name clashes with older versions of this lib', () => {
20+
function oldVersion() {
21+
class Foo {}
22+
return Foo;
23+
}
24+
25+
function newVersion() {
26+
class Foo {
27+
get [Symbol.toStringTag]() {
28+
return 'Foo';
29+
}
30+
}
31+
return Foo;
32+
}
33+
34+
const NewClass = newVersion();
35+
const OldClass = oldVersion();
36+
expect(instanceOfForDevelopment(new NewClass(), NewClass)).to.equal(true);
37+
expect(() => instanceOfForDevelopment(new OldClass(), NewClass)).to.throw();
38+
});
39+
40+
it('allows instances to have share the same constructor name', () => {
41+
function getMinifiedClass(tag: string) {
42+
class SomeNameAfterMinification {
43+
get [Symbol.toStringTag]() {
44+
return tag;
45+
}
46+
}
47+
return SomeNameAfterMinification;
48+
}
49+
50+
const Foo = getMinifiedClass('Foo');
51+
const Bar = getMinifiedClass('Bar');
52+
expect(instanceOfForDevelopment(new Foo(), Bar)).to.equal(false);
53+
expect(instanceOfForDevelopment(new Bar(), Foo)).to.equal(false);
54+
55+
const DuplicateOfFoo = getMinifiedClass('Foo');
56+
expect(() =>
57+
instanceOfForDevelopment(new DuplicateOfFoo(), Foo),
58+
).to.throw();
59+
expect(() =>
60+
instanceOfForDevelopment(new Foo(), DuplicateOfFoo),
61+
).to.throw();
62+
});
63+
64+
it('fails with descriptive error message', () => {
65+
function getFoo() {
66+
class Foo {
67+
get [Symbol.toStringTag]() {
68+
return 'Foo';
69+
}
70+
}
71+
return Foo;
72+
}
73+
const Foo1 = getFoo();
74+
const Foo2 = getFoo();
75+
76+
expect(() => instanceOfForDevelopment(new Foo1(), Foo2)).to.throw(
77+
/^Cannot use Foo "{}" from another module or realm./m,
78+
);
79+
expect(() => instanceOfForDevelopment(new Foo2(), Foo1)).to.throw(
80+
/^Cannot use Foo "{}" from another module or realm./m,
81+
);
82+
});
83+
});

0 commit comments

Comments
 (0)