@proteinjs/reflection-build 1.4.12 → 2.0.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/CHANGELOG.md +45 -0
- package/dist/src/BuildContract.d.ts +69 -0
- package/dist/src/BuildContract.js +393 -0
- package/dist/src/BuildContract.js.map +1 -0
- package/dist/src/ReflectionDoctor.d.ts +95 -0
- package/dist/src/ReflectionDoctor.js +588 -0
- package/dist/src/ReflectionDoctor.js.map +1 -0
- package/dist/src/build.d.ts +5 -1
- package/dist/src/build.js +13 -77
- package/dist/src/build.js.map +1 -1
- package/dist/src/codegen/writeGeneratedIndex.d.ts +25 -0
- package/dist/src/codegen/writeGeneratedIndex.js +89 -13
- package/dist/src/codegen/writeGeneratedIndex.js.map +1 -1
- package/dist/src/parser/createGraphBuilder.js +30 -5
- package/dist/src/parser/createGraphBuilder.js.map +1 -1
- package/dist/src/runBuild.js +12 -2
- package/dist/src/runBuild.js.map +1 -1
- package/dist/src/runDoctor.d.ts +2 -0
- package/dist/src/runDoctor.js +83 -0
- package/dist/src/runDoctor.js.map +1 -0
- package/dist/test/DependencySourceGraphGate.test.d.ts +1 -0
- package/dist/test/DependencySourceGraphGate.test.js +78 -0
- package/dist/test/DependencySourceGraphGate.test.js.map +1 -0
- package/dist/test/GraphEmitRelativePaths.test.d.ts +1 -0
- package/dist/test/GraphEmitRelativePaths.test.js +162 -0
- package/dist/test/GraphEmitRelativePaths.test.js.map +1 -0
- package/dist/test/ReflectionDoctor.test.d.ts +1 -0
- package/dist/test/ReflectionDoctor.test.js +316 -0
- package/dist/test/ReflectionDoctor.test.js.map +1 -0
- package/dist/test/TestSubpathContract.test.d.ts +1 -0
- package/dist/test/TestSubpathContract.test.js +260 -0
- package/dist/test/TestSubpathContract.test.js.map +1 -0
- package/dist/test/ValidateDontMutate.test.d.ts +1 -0
- package/dist/test/ValidateDontMutate.test.js +238 -0
- package/dist/test/ValidateDontMutate.test.js.map +1 -0
- package/dist/test/examples/source-repository/a/generated/index.d.ts +0 -8
- package/dist/test/examples/source-repository/a/generated/index.js +1 -9
- package/dist/test/examples/source-repository/a/generated/index.js.map +1 -1
- package/dist/test/examples/source-repository/b/generated/index.d.ts +0 -8
- package/dist/test/examples/source-repository/b/generated/index.js +1 -9
- package/dist/test/examples/source-repository/b/generated/index.js.map +1 -1
- package/package.json +5 -4
- package/src/BuildContract.ts +384 -0
- package/src/ReflectionDoctor.ts +589 -0
- package/src/build.ts +19 -56
- package/src/codegen/writeGeneratedIndex.ts +80 -9
- package/src/parser/createGraphBuilder.ts +9 -4
- package/src/runBuild.ts +7 -1
- package/src/runDoctor.ts +34 -0
- package/test/DependencySourceGraphGate.test.ts +61 -0
- package/test/GraphEmitRelativePaths.test.ts +95 -0
- package/test/ReflectionDoctor.test.ts +193 -0
- package/test/TestSubpathContract.test.ts +148 -0
- package/test/ValidateDontMutate.test.ts +126 -0
- package/test/examples/source-repository/a/dist/BuildContract.d.ts +69 -0
- package/test/examples/source-repository/a/dist/BuildContract.js +393 -0
- package/test/examples/source-repository/a/dist/BuildContract.js.map +1 -0
- package/test/examples/source-repository/a/dist/build.d.ts +5 -1
- package/test/examples/source-repository/a/dist/build.js +13 -77
- package/test/examples/source-repository/a/dist/build.js.map +1 -1
- package/test/examples/source-repository/a/dist/codegen/writeGeneratedIndex.d.ts +25 -0
- package/test/examples/source-repository/a/dist/codegen/writeGeneratedIndex.js +89 -13
- package/test/examples/source-repository/a/dist/codegen/writeGeneratedIndex.js.map +1 -1
- package/test/examples/source-repository/a/dist/generated/index.d.ts +0 -8
- package/test/examples/source-repository/a/dist/generated/index.js +1 -9
- package/test/examples/source-repository/a/dist/generated/index.js.map +1 -1
- package/test/examples/source-repository/a/dist/parser/createGraphBuilder.js +30 -5
- package/test/examples/source-repository/a/dist/parser/createGraphBuilder.js.map +1 -1
- package/test/examples/source-repository/a/dist/runBuild.js +12 -2
- package/test/examples/source-repository/a/dist/runBuild.js.map +1 -1
- package/test/examples/source-repository/a/generated/index.ts +1 -9
- package/test/examples/source-repository/b/dist/BuildContract.d.ts +69 -0
- package/test/examples/source-repository/b/dist/BuildContract.js +393 -0
- package/test/examples/source-repository/b/dist/BuildContract.js.map +1 -0
- package/test/examples/source-repository/b/dist/build.d.ts +5 -1
- package/test/examples/source-repository/b/dist/build.js +13 -77
- package/test/examples/source-repository/b/dist/build.js.map +1 -1
- package/test/examples/source-repository/b/dist/codegen/writeGeneratedIndex.d.ts +25 -0
- package/test/examples/source-repository/b/dist/codegen/writeGeneratedIndex.js +89 -13
- package/test/examples/source-repository/b/dist/codegen/writeGeneratedIndex.js.map +1 -1
- package/test/examples/source-repository/b/dist/generated/index.d.ts +0 -8
- package/test/examples/source-repository/b/dist/generated/index.js +1 -9
- package/test/examples/source-repository/b/dist/generated/index.js.map +1 -1
- package/test/examples/source-repository/b/dist/parser/createGraphBuilder.js +30 -5
- package/test/examples/source-repository/b/dist/parser/createGraphBuilder.js.map +1 -1
- package/test/examples/source-repository/b/dist/runBuild.js +12 -2
- package/test/examples/source-repository/b/dist/runBuild.js.map +1 -1
- package/test/examples/source-repository/b/generated/index.ts +1 -10
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { build } from '../src/build';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* The ./test subpath contract (task #104 §3.2, the d51556f0 class): the test subpath is a
|
|
8
|
+
* reflection-build feature, so reflection-build owns the COMPLETE artifact contract for it —
|
|
9
|
+
* root stubs, exports/typesVersions mappings, files[] entries, tsconfig excludes — instead of
|
|
10
|
+
* every package hand-rolling the d51556f0 shims and drifting.
|
|
11
|
+
*
|
|
12
|
+
* Contract pieces, validated/emitted on every subpath build (primary root != 'src'):
|
|
13
|
+
* - root <root>.js / <root>.d.ts stubs re-exporting dist/generated/<root>/index — win the
|
|
14
|
+
* node10/paths-mapped file lookup so consumers resolve built dist, never sources (TS6059)
|
|
15
|
+
* - exports: "." and "./<root>" mapped to the built artifacts (node16/modern resolution)
|
|
16
|
+
* - typesVersions: "<root>" mapped for node10 TYPES resolution
|
|
17
|
+
* - files[]: stub entries added when a files allowlist exists (published tarballs keep them)
|
|
18
|
+
* - tsconfig exclude: the stubs never become compiler inputs
|
|
19
|
+
*
|
|
20
|
+
* Red-before-green: against pre-change code nothing emits any of this — every assertion fails.
|
|
21
|
+
*/
|
|
22
|
+
describe('test-subpath contract', () => {
|
|
23
|
+
let pkgDir: string;
|
|
24
|
+
const savedEnv: { [key: string]: string | undefined } = {};
|
|
25
|
+
|
|
26
|
+
const writeFixture = (packageJson: any) => {
|
|
27
|
+
pkgDir = fs.mkdtempSync(path.join(os.tmpdir(), 'refl-subpath-'));
|
|
28
|
+
fs.writeFileSync(path.join(pkgDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
29
|
+
fs.mkdirSync(path.join(pkgDir, 'src'));
|
|
30
|
+
fs.writeFileSync(path.join(pkgDir, 'src', 'index.ts'), `export * from './Example';\n`);
|
|
31
|
+
fs.writeFileSync(path.join(pkgDir, 'src', 'Example.ts'), `export class Example {}\n`);
|
|
32
|
+
fs.mkdirSync(path.join(pkgDir, 'test'));
|
|
33
|
+
fs.writeFileSync(path.join(pkgDir, 'test', 'index.ts'), `export * from './TestHelper';\n`);
|
|
34
|
+
fs.writeFileSync(path.join(pkgDir, 'test', 'TestHelper.ts'), `export class TestHelper {}\n`);
|
|
35
|
+
process.env.INIT_CWD = pkgDir;
|
|
36
|
+
process.env.REFLECTION_SOURCE_DIRS = 'test,src';
|
|
37
|
+
process.env.REFLECTION_EXPORT_FROM = 'test/index.ts';
|
|
38
|
+
};
|
|
39
|
+
|
|
40
|
+
const readPackageJson = () => JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8'));
|
|
41
|
+
const readTsconfig = () => JSON.parse(fs.readFileSync(path.join(pkgDir, 'tsconfig.json'), 'utf-8'));
|
|
42
|
+
|
|
43
|
+
beforeEach(() => {
|
|
44
|
+
for (const key of ['INIT_CWD', 'REFLECTION_SOURCE_DIRS', 'REFLECTION_EXPORT_FROM', 'REFLECTION_DIST_DIR']) {
|
|
45
|
+
savedEnv[key] = process.env[key];
|
|
46
|
+
delete process.env[key];
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
afterEach(() => {
|
|
51
|
+
for (const key of Object.keys(savedEnv)) {
|
|
52
|
+
if (typeof savedEnv[key] === 'undefined') {
|
|
53
|
+
delete process.env[key];
|
|
54
|
+
} else {
|
|
55
|
+
process.env[key] = savedEnv[key];
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
fs.rmdirSync(pkgDir, { recursive: true });
|
|
59
|
+
});
|
|
60
|
+
|
|
61
|
+
it('emits root stubs that resolve the subpath to built dist in every install shape', async () => {
|
|
62
|
+
writeFixture({ name: '@test/subpath-fixture', version: '1.0.0' });
|
|
63
|
+
await build();
|
|
64
|
+
|
|
65
|
+
const stubJs = fs.readFileSync(path.join(pkgDir, 'test.js'), 'utf-8');
|
|
66
|
+
expect(stubJs).toContain(`require('./dist/generated/test/index.js')`);
|
|
67
|
+
const stubDts = fs.readFileSync(path.join(pkgDir, 'test.d.ts'), 'utf-8');
|
|
68
|
+
expect(stubDts).toContain(`export * from './dist/generated/test/index'`);
|
|
69
|
+
});
|
|
70
|
+
|
|
71
|
+
it('maps the subpath in exports and typesVersions', async () => {
|
|
72
|
+
writeFixture({ name: '@test/subpath-fixture', version: '1.0.0' });
|
|
73
|
+
await build();
|
|
74
|
+
|
|
75
|
+
const packageJson = readPackageJson();
|
|
76
|
+
expect(packageJson.exports['.']).toEqual({
|
|
77
|
+
types: './dist/generated/index.d.ts',
|
|
78
|
+
default: './dist/generated/index.js',
|
|
79
|
+
});
|
|
80
|
+
expect(packageJson.exports['./test']).toEqual({
|
|
81
|
+
types: './dist/generated/test/index.d.ts',
|
|
82
|
+
default: './dist/generated/test/index.js',
|
|
83
|
+
});
|
|
84
|
+
expect(packageJson.typesVersions['*']['test']).toEqual(['dist/generated/test/index.d.ts']);
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
it('keeps extra exports subpaths a package added on its own', async () => {
|
|
88
|
+
writeFixture({
|
|
89
|
+
name: '@test/subpath-fixture',
|
|
90
|
+
version: '1.0.0',
|
|
91
|
+
exports: { './extra': './dist/extra.js' },
|
|
92
|
+
});
|
|
93
|
+
await build();
|
|
94
|
+
|
|
95
|
+
const packageJson = readPackageJson();
|
|
96
|
+
expect(packageJson.exports['./extra']).toBe('./dist/extra.js');
|
|
97
|
+
expect(packageJson.exports['./test']).toEqual({
|
|
98
|
+
types: './dist/generated/test/index.d.ts',
|
|
99
|
+
default: './dist/generated/test/index.js',
|
|
100
|
+
});
|
|
101
|
+
});
|
|
102
|
+
|
|
103
|
+
it('adds stub entries to an existing files allowlist, and leaves absent files[] absent', async () => {
|
|
104
|
+
writeFixture({ name: '@test/subpath-fixture', version: '1.0.0', files: ['dist/**'] });
|
|
105
|
+
await build();
|
|
106
|
+
expect(readPackageJson().files).toEqual(expect.arrayContaining(['dist/**', 'test.js', 'test.d.ts']));
|
|
107
|
+
|
|
108
|
+
fs.rmdirSync(pkgDir, { recursive: true });
|
|
109
|
+
writeFixture({ name: '@test/subpath-fixture', version: '1.0.0' });
|
|
110
|
+
await build();
|
|
111
|
+
expect(readPackageJson().files).toBeUndefined();
|
|
112
|
+
});
|
|
113
|
+
|
|
114
|
+
it('excludes the stubs from compilation in tsconfig without clobbering default excludes', async () => {
|
|
115
|
+
writeFixture({ name: '@test/subpath-fixture', version: '1.0.0' });
|
|
116
|
+
await build();
|
|
117
|
+
|
|
118
|
+
const tsconfig = readTsconfig();
|
|
119
|
+
expect(tsconfig.exclude).toEqual(expect.arrayContaining(['node_modules', 'test.js', 'test.d.ts']));
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
it('fails friendly on a conflicting subpath mapping and leaves package.json untouched', async () => {
|
|
123
|
+
writeFixture({
|
|
124
|
+
name: '@test/subpath-fixture',
|
|
125
|
+
version: '1.0.0',
|
|
126
|
+
exports: { './test': './dist/somewhere-else.js' },
|
|
127
|
+
});
|
|
128
|
+
fs.mkdirSync(path.join(pkgDir, 'dist'));
|
|
129
|
+
fs.writeFileSync(path.join(pkgDir, 'dist', 'somewhere-else.js'), 'module.exports = {};\n');
|
|
130
|
+
const before = fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8');
|
|
131
|
+
|
|
132
|
+
await expect(build()).rejects.toThrow(/--fix/);
|
|
133
|
+
expect(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8')).toBe(before);
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
it('is idempotent: a second build changes nothing', async () => {
|
|
137
|
+
writeFixture({ name: '@test/subpath-fixture', version: '1.0.0', files: ['dist/**'] });
|
|
138
|
+
await build();
|
|
139
|
+
const packageJsonAfterFirst = fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8');
|
|
140
|
+
const stubAfterFirst = fs.readFileSync(path.join(pkgDir, 'test.js'), 'utf-8');
|
|
141
|
+
const tsconfigAfterFirst = fs.readFileSync(path.join(pkgDir, 'tsconfig.json'), 'utf-8');
|
|
142
|
+
|
|
143
|
+
await build();
|
|
144
|
+
expect(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8')).toBe(packageJsonAfterFirst);
|
|
145
|
+
expect(fs.readFileSync(path.join(pkgDir, 'test.js'), 'utf-8')).toBe(stubAfterFirst);
|
|
146
|
+
expect(fs.readFileSync(path.join(pkgDir, 'tsconfig.json'), 'utf-8')).toBe(tsconfigAfterFirst);
|
|
147
|
+
});
|
|
148
|
+
});
|
|
@@ -0,0 +1,126 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as os from 'os';
|
|
3
|
+
import * as path from 'path';
|
|
4
|
+
import { build } from '../src/build';
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Validate-don't-mutate (task #104 §3.3): the build computes the package-config contract and
|
|
8
|
+
* VALIDATES it instead of silently rewriting files.
|
|
9
|
+
*
|
|
10
|
+
* The founder's UX bar:
|
|
11
|
+
* - ABSENCE is not a mismatch — a fresh package missing main/types gets them SET (today's
|
|
12
|
+
* behavior kept; the copy-a-package workflow survives).
|
|
13
|
+
* - The build errors ONLY on conflicting existing values, and the error is friendly: plain
|
|
14
|
+
* words, why it matters, the exact lines to paste, `--fix` offered first.
|
|
15
|
+
* - `reflection-build --fix` writes the contract; a second run is a no-op.
|
|
16
|
+
*
|
|
17
|
+
* Red-before-green: against pre-change code the conflict tests fail because the mutating
|
|
18
|
+
* build succeeds and silently rewrites package.json.
|
|
19
|
+
*/
|
|
20
|
+
describe("validate-don't-mutate", () => {
|
|
21
|
+
let pkgDir: string;
|
|
22
|
+
const savedEnv: { [key: string]: string | undefined } = {};
|
|
23
|
+
|
|
24
|
+
const writeFixture = (packageJson: any) => {
|
|
25
|
+
pkgDir = fs.mkdtempSync(path.join(os.tmpdir(), 'refl-validate-'));
|
|
26
|
+
fs.writeFileSync(path.join(pkgDir, 'package.json'), JSON.stringify(packageJson, null, 2));
|
|
27
|
+
fs.mkdirSync(path.join(pkgDir, 'src'));
|
|
28
|
+
fs.writeFileSync(path.join(pkgDir, 'src', 'index.ts'), `export * from './Example';\n`);
|
|
29
|
+
fs.writeFileSync(path.join(pkgDir, 'src', 'Example.ts'), `export class Example {}\n`);
|
|
30
|
+
process.env.INIT_CWD = pkgDir;
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
const readPackageJson = () => JSON.parse(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8'));
|
|
34
|
+
|
|
35
|
+
beforeEach(() => {
|
|
36
|
+
for (const key of ['INIT_CWD', 'REFLECTION_SOURCE_DIRS', 'REFLECTION_EXPORT_FROM', 'REFLECTION_DIST_DIR']) {
|
|
37
|
+
savedEnv[key] = process.env[key];
|
|
38
|
+
delete process.env[key];
|
|
39
|
+
}
|
|
40
|
+
});
|
|
41
|
+
|
|
42
|
+
afterEach(() => {
|
|
43
|
+
for (const key of Object.keys(savedEnv)) {
|
|
44
|
+
if (typeof savedEnv[key] === 'undefined') {
|
|
45
|
+
delete process.env[key];
|
|
46
|
+
} else {
|
|
47
|
+
process.env[key] = savedEnv[key];
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
fs.rmdirSync(pkgDir, { recursive: true });
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('fails the build on a conflicting main and leaves package.json untouched', async () => {
|
|
54
|
+
writeFixture({ name: '@test/validate-fixture', version: '1.0.0', main: './dist/custom-entry.js' });
|
|
55
|
+
// The conflicting value points at a REAL file — a deliberate setting, not boilerplate.
|
|
56
|
+
fs.mkdirSync(path.join(pkgDir, 'dist'));
|
|
57
|
+
fs.writeFileSync(path.join(pkgDir, 'dist', 'custom-entry.js'), 'module.exports = {};\n');
|
|
58
|
+
const before = fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8');
|
|
59
|
+
|
|
60
|
+
await expect(build()).rejects.toThrow(/--fix/);
|
|
61
|
+
expect(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8')).toBe(before);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
it('the conflict error is friendly: offers --fix first, then the exact lines to paste', async () => {
|
|
65
|
+
writeFixture({ name: '@test/validate-fixture', version: '1.0.0', main: './dist/custom-entry.js' });
|
|
66
|
+
fs.mkdirSync(path.join(pkgDir, 'dist'));
|
|
67
|
+
fs.writeFileSync(path.join(pkgDir, 'dist', 'custom-entry.js'), 'module.exports = {};\n');
|
|
68
|
+
|
|
69
|
+
let message = '';
|
|
70
|
+
try {
|
|
71
|
+
await build();
|
|
72
|
+
} catch (error: any) {
|
|
73
|
+
message = error.message;
|
|
74
|
+
}
|
|
75
|
+
expect(message).toContain('reflection-build --fix');
|
|
76
|
+
expect(message).toContain('"main": "./dist/generated/index.js"');
|
|
77
|
+
expect(message).toContain('"types": "./dist/generated/index.d.ts"');
|
|
78
|
+
// --fix is the first remedy offered, before the paste-able lines.
|
|
79
|
+
expect(message.indexOf('--fix')).toBeGreaterThan(-1);
|
|
80
|
+
expect(message.indexOf('--fix')).toBeLessThan(message.indexOf('"main": "./dist/generated/index.js"'));
|
|
81
|
+
// Plain words about why it matters, not just a diff.
|
|
82
|
+
expect(message.toLowerCase()).toContain('consumer');
|
|
83
|
+
});
|
|
84
|
+
|
|
85
|
+
it('sets absent main/types silently — the fresh/copied-package workflow survives', async () => {
|
|
86
|
+
writeFixture({ name: '@test/validate-fixture', version: '1.0.0' });
|
|
87
|
+
await build();
|
|
88
|
+
const packageJson = readPackageJson();
|
|
89
|
+
expect(packageJson.main).toBe('./dist/generated/index.js');
|
|
90
|
+
expect(packageJson.types).toBe('./dist/generated/index.d.ts');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('treats npm-init boilerplate (main pointing at a file that does not exist) as absence', async () => {
|
|
94
|
+
writeFixture({ name: '@test/validate-fixture', version: '1.0.0', main: 'index.js' });
|
|
95
|
+
await build();
|
|
96
|
+
expect(readPackageJson().main).toBe('./dist/generated/index.js');
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it('accepts a package already on contract without touching it', async () => {
|
|
100
|
+
writeFixture({
|
|
101
|
+
name: '@test/validate-fixture',
|
|
102
|
+
version: '1.0.0',
|
|
103
|
+
main: './dist/generated/index.js',
|
|
104
|
+
types: './dist/generated/index.d.ts',
|
|
105
|
+
});
|
|
106
|
+
await build();
|
|
107
|
+
const first = fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8');
|
|
108
|
+
await build();
|
|
109
|
+
expect(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8')).toBe(first);
|
|
110
|
+
});
|
|
111
|
+
|
|
112
|
+
it('--fix rewrites conflicting values to the contract; a second run is a no-op', async () => {
|
|
113
|
+
writeFixture({ name: '@test/validate-fixture', version: '1.0.0', main: './dist/custom-entry.js' });
|
|
114
|
+
fs.mkdirSync(path.join(pkgDir, 'dist'));
|
|
115
|
+
fs.writeFileSync(path.join(pkgDir, 'dist', 'custom-entry.js'), 'module.exports = {};\n');
|
|
116
|
+
|
|
117
|
+
await build({ fix: true });
|
|
118
|
+
const packageJson = readPackageJson();
|
|
119
|
+
expect(packageJson.main).toBe('./dist/generated/index.js');
|
|
120
|
+
expect(packageJson.types).toBe('./dist/generated/index.d.ts');
|
|
121
|
+
|
|
122
|
+
const afterFix = fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8');
|
|
123
|
+
await build();
|
|
124
|
+
expect(fs.readFileSync(path.join(pkgDir, 'package.json'), 'utf-8')).toBe(afterFix);
|
|
125
|
+
});
|
|
126
|
+
});
|
|
@@ -0,0 +1,69 @@
|
|
|
1
|
+
export interface BuildContractOptions {
|
|
2
|
+
/** Root of the package being built (contains package.json) */
|
|
3
|
+
packageDir: string;
|
|
4
|
+
/** First REFLECTION_SOURCE_DIRS entry; 'src' for prod builds */
|
|
5
|
+
primaryRoot: string;
|
|
6
|
+
/** REFLECTION_DIST_DIR or 'dist' */
|
|
7
|
+
distDirRel: string;
|
|
8
|
+
/** Absolute path to the generated index .ts this invocation emits */
|
|
9
|
+
generatedIndexPath: string;
|
|
10
|
+
/** Write conflicting values to the contract instead of failing the build */
|
|
11
|
+
fix: boolean;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* The package-config contract of a reflection build (task #104 §3.3, validate-don't-mutate).
|
|
15
|
+
*
|
|
16
|
+
* A build's artifact is only usable if the package's config points at it, so reflection-build
|
|
17
|
+
* owns that config as a contract instead of silently rewriting files on every build:
|
|
18
|
+
*
|
|
19
|
+
* - ABSENT config is completed in place (a fresh or copied package gets main/types, subpath
|
|
20
|
+
* mappings, stubs — the zero-ceremony workflow survives).
|
|
21
|
+
* - EXISTING config that conflicts with the contract fails the build with a friendly error
|
|
22
|
+
* (plain words, why it matters, the exact lines to paste, `--fix` offered first) and leaves
|
|
23
|
+
* every file untouched.
|
|
24
|
+
* - `reflection-build --fix` writes the contract over conflicting values; it is idempotent.
|
|
25
|
+
*
|
|
26
|
+
* Prod builds (primary root 'src') own main/types + the tsconfig include. Subpath builds
|
|
27
|
+
* (primary root e.g. 'test') own the complete subpath contract — root <root>.js/<root>.d.ts
|
|
28
|
+
* stubs, exports/typesVersions mappings, files[] entries, tsconfig excludes (the d51556f0
|
|
29
|
+
* class, generalized): consumers must resolve the subpath to built dist in every install
|
|
30
|
+
* shape and resolution mode, never to sources.
|
|
31
|
+
*/
|
|
32
|
+
export declare class BuildContract {
|
|
33
|
+
private readonly options;
|
|
34
|
+
constructor(options: BuildContractOptions);
|
|
35
|
+
apply(): Promise<void>;
|
|
36
|
+
private applyPackageJson;
|
|
37
|
+
/** Prod builds: main/types point at the built reflection artifact. */
|
|
38
|
+
private settleEntryPoints;
|
|
39
|
+
/** Subpath builds: exports map "." and "./<root>" to the built artifacts. */
|
|
40
|
+
private settleSubpathExports;
|
|
41
|
+
/** Subpath builds: typesVersions carries the subpath's types for node10 resolution. */
|
|
42
|
+
private settleTypesVersions;
|
|
43
|
+
/** Subpath builds: an existing files allowlist must keep shipping the root stubs. */
|
|
44
|
+
private settleFilesAllowlist;
|
|
45
|
+
/**
|
|
46
|
+
* Subpath builds: root stubs that win the node10/paths-mapped file lookup, so every install
|
|
47
|
+
* shape resolves the subpath to built dist — never to sources (the TS6059/ts-jest class).
|
|
48
|
+
*/
|
|
49
|
+
private applyStubs;
|
|
50
|
+
/**
|
|
51
|
+
* tsconfig: include the generated index; on subpath builds, exclude the root stubs from
|
|
52
|
+
* compilation. List membership only — nothing here can conflict.
|
|
53
|
+
*/
|
|
54
|
+
private applyTsconfig;
|
|
55
|
+
/**
|
|
56
|
+
* Settle one string field: absent → set; equal → ok; a dangling value (points at a file
|
|
57
|
+
* that does not exist — npm init's boilerplate `"main": "index.js"`, a stale copy) → set;
|
|
58
|
+
* a value that points at a real, different file is a deliberate setting → conflict.
|
|
59
|
+
*/
|
|
60
|
+
private settleString;
|
|
61
|
+
private formatConflicts;
|
|
62
|
+
private isProd;
|
|
63
|
+
private prodIndexJs;
|
|
64
|
+
private prodIndexDts;
|
|
65
|
+
private subpathIndexJs;
|
|
66
|
+
private subpathIndexDts;
|
|
67
|
+
private stubJsName;
|
|
68
|
+
private stubDtsName;
|
|
69
|
+
}
|