@proteinjs/reflection-build 1.5.0 → 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 +29 -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 +18 -0
- package/dist/src/codegen/writeGeneratedIndex.js +58 -15
- 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/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.js +1 -1
- package/dist/test/examples/source-repository/a/generated/index.js.map +1 -1
- package/dist/test/examples/source-repository/b/generated/index.js +1 -1
- 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 +51 -13
- package/src/parser/createGraphBuilder.ts +9 -4
- package/src/runBuild.ts +7 -1
- package/src/runDoctor.ts +34 -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 +18 -0
- package/test/examples/source-repository/a/dist/codegen/writeGeneratedIndex.js +58 -15
- package/test/examples/source-repository/a/dist/codegen/writeGeneratedIndex.js.map +1 -1
- package/test/examples/source-repository/a/dist/generated/index.js +1 -1
- 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 -1
- 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 +18 -0
- package/test/examples/source-repository/b/dist/codegen/writeGeneratedIndex.js +58 -15
- package/test/examples/source-repository/b/dist/codegen/writeGeneratedIndex.js.map +1 -1
- package/test/examples/source-repository/b/dist/generated/index.js +1 -1
- 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 -1
|
@@ -0,0 +1,384 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
const tsconfigTemplate = require('./tsconfigTemplate.json');
|
|
4
|
+
|
|
5
|
+
export interface BuildContractOptions {
|
|
6
|
+
/** Root of the package being built (contains package.json) */
|
|
7
|
+
packageDir: string;
|
|
8
|
+
/** First REFLECTION_SOURCE_DIRS entry; 'src' for prod builds */
|
|
9
|
+
primaryRoot: string;
|
|
10
|
+
/** REFLECTION_DIST_DIR or 'dist' */
|
|
11
|
+
distDirRel: string;
|
|
12
|
+
/** Absolute path to the generated index .ts this invocation emits */
|
|
13
|
+
generatedIndexPath: string;
|
|
14
|
+
/** Write conflicting values to the contract instead of failing the build */
|
|
15
|
+
fix: boolean;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
interface PendingWrite {
|
|
19
|
+
filePath: string;
|
|
20
|
+
content: string;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
interface Conflict {
|
|
24
|
+
file: string;
|
|
25
|
+
found: string;
|
|
26
|
+
expected: string;
|
|
27
|
+
whyItMatters: string;
|
|
28
|
+
pasteBlock: string;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* The package-config contract of a reflection build (task #104 §3.3, validate-don't-mutate).
|
|
33
|
+
*
|
|
34
|
+
* A build's artifact is only usable if the package's config points at it, so reflection-build
|
|
35
|
+
* owns that config as a contract instead of silently rewriting files on every build:
|
|
36
|
+
*
|
|
37
|
+
* - ABSENT config is completed in place (a fresh or copied package gets main/types, subpath
|
|
38
|
+
* mappings, stubs — the zero-ceremony workflow survives).
|
|
39
|
+
* - EXISTING config that conflicts with the contract fails the build with a friendly error
|
|
40
|
+
* (plain words, why it matters, the exact lines to paste, `--fix` offered first) and leaves
|
|
41
|
+
* every file untouched.
|
|
42
|
+
* - `reflection-build --fix` writes the contract over conflicting values; it is idempotent.
|
|
43
|
+
*
|
|
44
|
+
* Prod builds (primary root 'src') own main/types + the tsconfig include. Subpath builds
|
|
45
|
+
* (primary root e.g. 'test') own the complete subpath contract — root <root>.js/<root>.d.ts
|
|
46
|
+
* stubs, exports/typesVersions mappings, files[] entries, tsconfig excludes (the d51556f0
|
|
47
|
+
* class, generalized): consumers must resolve the subpath to built dist in every install
|
|
48
|
+
* shape and resolution mode, never to sources.
|
|
49
|
+
*/
|
|
50
|
+
export class BuildContract {
|
|
51
|
+
constructor(private readonly options: BuildContractOptions) {}
|
|
52
|
+
|
|
53
|
+
async apply(): Promise<void> {
|
|
54
|
+
// Two-phase: collect every pending write first, then flush only if conflict-free (or
|
|
55
|
+
// fixing). A conflicting build must leave EVERY file untouched — including files whose
|
|
56
|
+
// own changes were mere absence-completions.
|
|
57
|
+
const conflicts: Conflict[] = [];
|
|
58
|
+
const writes: PendingWrite[] = [];
|
|
59
|
+
this.applyPackageJson(conflicts, writes);
|
|
60
|
+
this.applyStubs(conflicts, writes);
|
|
61
|
+
this.applyTsconfig(writes);
|
|
62
|
+
if (conflicts.length > 0) {
|
|
63
|
+
throw new Error(this.formatConflicts(conflicts));
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
for (const write of writes) {
|
|
67
|
+
fs.writeFileSync(write.filePath, write.content);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
private applyPackageJson(conflicts: Conflict[], writes: PendingWrite[]): void {
|
|
72
|
+
const packageJsonPath = path.join(this.options.packageDir, 'package.json');
|
|
73
|
+
if (!fs.existsSync(packageJsonPath)) {
|
|
74
|
+
throw new Error(`package.json does not exist, run \`npm init -y\` to create one`);
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
const original = fs.readFileSync(packageJsonPath, 'utf-8');
|
|
78
|
+
const packageJson = JSON.parse(original);
|
|
79
|
+
|
|
80
|
+
if (this.isProd()) {
|
|
81
|
+
this.settleEntryPoints(packageJson, conflicts);
|
|
82
|
+
} else {
|
|
83
|
+
this.settleSubpathExports(packageJson, conflicts);
|
|
84
|
+
this.settleTypesVersions(packageJson, conflicts);
|
|
85
|
+
this.settleFilesAllowlist(packageJson);
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
const updated = JSON.stringify(packageJson, null, 2);
|
|
89
|
+
if (updated !== JSON.stringify(JSON.parse(original), null, 2)) {
|
|
90
|
+
writes.push({ filePath: packageJsonPath, content: updated });
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
|
|
94
|
+
/** Prod builds: main/types point at the built reflection artifact. */
|
|
95
|
+
private settleEntryPoints(packageJson: any, conflicts: Conflict[]): void {
|
|
96
|
+
const pasteBlock = [` "main": "${this.prodIndexJs()}",`, ` "types": "${this.prodIndexDts()}"`].join('\n');
|
|
97
|
+
const whyItMatters =
|
|
98
|
+
'Every consumer loads this package through main/types; pointing them anywhere but the ' +
|
|
99
|
+
'built reflection artifact ships a package that loads the wrong code.';
|
|
100
|
+
this.settleString(packageJson, 'main', this.prodIndexJs(), conflicts, whyItMatters, pasteBlock);
|
|
101
|
+
this.settleString(packageJson, 'types', this.prodIndexDts(), conflicts, whyItMatters, pasteBlock);
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/** Subpath builds: exports map "." and "./<root>" to the built artifacts. */
|
|
105
|
+
private settleSubpathExports(packageJson: any, conflicts: Conflict[]): void {
|
|
106
|
+
const root = this.options.primaryRoot;
|
|
107
|
+
const expectedExports: { [subpath: string]: { types: string; default: string } } = {
|
|
108
|
+
'.': { types: this.prodIndexDts(), default: this.prodIndexJs() },
|
|
109
|
+
[`./${root}`]: { types: this.subpathIndexDts(), default: this.subpathIndexJs() },
|
|
110
|
+
};
|
|
111
|
+
const whyItMatters =
|
|
112
|
+
`Consumers import '<package>/${root}' through these mappings; a wrong mapping resolves ` +
|
|
113
|
+
'to the wrong files (or to sources), breaking consumer builds.';
|
|
114
|
+
|
|
115
|
+
if (packageJson.exports == null) {
|
|
116
|
+
packageJson.exports = expectedExports;
|
|
117
|
+
return;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
for (const subpath of Object.keys(expectedExports)) {
|
|
121
|
+
const expected = expectedExports[subpath];
|
|
122
|
+
const current = packageJson.exports[subpath];
|
|
123
|
+
if (current == null) {
|
|
124
|
+
packageJson.exports[subpath] = expected;
|
|
125
|
+
} else if (JSON.stringify(current) !== JSON.stringify(expected)) {
|
|
126
|
+
if (this.options.fix) {
|
|
127
|
+
packageJson.exports[subpath] = expected;
|
|
128
|
+
} else {
|
|
129
|
+
conflicts.push({
|
|
130
|
+
file: 'package.json',
|
|
131
|
+
found: `"exports" maps "${subpath}" to ${JSON.stringify(current)}`,
|
|
132
|
+
expected: JSON.stringify(expected),
|
|
133
|
+
whyItMatters,
|
|
134
|
+
pasteBlock: ` "exports": ${JSON.stringify(expectedExports, null, 2).replace(/\n/g, '\n ')}`,
|
|
135
|
+
});
|
|
136
|
+
}
|
|
137
|
+
}
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
/** Subpath builds: typesVersions carries the subpath's types for node10 resolution. */
|
|
142
|
+
private settleTypesVersions(packageJson: any, conflicts: Conflict[]): void {
|
|
143
|
+
const root = this.options.primaryRoot;
|
|
144
|
+
// No leading './' by typesVersions convention.
|
|
145
|
+
const expected = [this.subpathIndexDts().replace(/^\.\//, '')];
|
|
146
|
+
const expectedBlock: any = { '*': { [root]: expected } };
|
|
147
|
+
|
|
148
|
+
if (packageJson.typesVersions == null) {
|
|
149
|
+
packageJson.typesVersions = expectedBlock;
|
|
150
|
+
return;
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
if (packageJson.typesVersions['*'] == null) {
|
|
154
|
+
packageJson.typesVersions['*'] = expectedBlock['*'];
|
|
155
|
+
return;
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
const current = packageJson.typesVersions['*'][root];
|
|
159
|
+
if (current == null) {
|
|
160
|
+
packageJson.typesVersions['*'][root] = expected;
|
|
161
|
+
} else if (JSON.stringify(current) !== JSON.stringify(expected)) {
|
|
162
|
+
if (this.options.fix) {
|
|
163
|
+
packageJson.typesVersions['*'][root] = expected;
|
|
164
|
+
} else {
|
|
165
|
+
conflicts.push({
|
|
166
|
+
file: 'package.json',
|
|
167
|
+
found: `"typesVersions" maps "${root}" to ${JSON.stringify(current)}`,
|
|
168
|
+
expected: JSON.stringify(expected),
|
|
169
|
+
whyItMatters:
|
|
170
|
+
`Editors and node10-resolution consumers find '<package>/${root}' types through this ` +
|
|
171
|
+
'mapping; a wrong mapping breaks type resolution for every consumer.',
|
|
172
|
+
pasteBlock: ` "typesVersions": ${JSON.stringify(expectedBlock, null, 2).replace(/\n/g, '\n ')}`,
|
|
173
|
+
});
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
/** Subpath builds: an existing files allowlist must keep shipping the root stubs. */
|
|
179
|
+
private settleFilesAllowlist(packageJson: any): void {
|
|
180
|
+
if (!Array.isArray(packageJson.files)) {
|
|
181
|
+
// No allowlist means npm publishes the stubs already; adding one would change what
|
|
182
|
+
// gets published, which is not this contract's call.
|
|
183
|
+
return;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
for (const stub of [this.stubJsName(), this.stubDtsName()]) {
|
|
187
|
+
if (!packageJson.files.includes(stub)) {
|
|
188
|
+
packageJson.files.push(stub);
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
/**
|
|
194
|
+
* Subpath builds: root stubs that win the node10/paths-mapped file lookup, so every install
|
|
195
|
+
* shape resolves the subpath to built dist — never to sources (the TS6059/ts-jest class).
|
|
196
|
+
*/
|
|
197
|
+
private applyStubs(conflicts: Conflict[], writes: PendingWrite[]): void {
|
|
198
|
+
if (this.isProd()) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const root = this.options.primaryRoot;
|
|
203
|
+
const targetNoExt = `./${this.options.distDirRel}/generated/${root}/index`;
|
|
204
|
+
const whyItMatters =
|
|
205
|
+
`Consumers compiled with node10/classic resolution (or a tsconfig paths mapping) resolve ` +
|
|
206
|
+
`'<package>/${root}' as a file path, bypassing the exports map; without a root stub that ` +
|
|
207
|
+
'lands on sources in workspace-symlinked installs (TS6059 breaks the consumer build).';
|
|
208
|
+
|
|
209
|
+
const stubJs = {
|
|
210
|
+
file: this.stubJsName(),
|
|
211
|
+
content:
|
|
212
|
+
`// Node10/classic-resolution stub for the \`./${root}\` subpath, owned by reflection-build.\n` +
|
|
213
|
+
`// Consumers that bypass the exports map (node10 resolution, tsconfig paths mappings) resolve\n` +
|
|
214
|
+
`// this file path first, so every install shape loads the built dist — never sources.\n` +
|
|
215
|
+
`module.exports = require('${targetNoExt}.js');\n`,
|
|
216
|
+
};
|
|
217
|
+
const stubDts = {
|
|
218
|
+
file: this.stubDtsName(),
|
|
219
|
+
content:
|
|
220
|
+
`// Types counterpart of ${this.stubJsName()} — see that file for why this stub exists.\n` +
|
|
221
|
+
`export * from '${targetNoExt}';\n`,
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
for (const stub of [stubJs, stubDts]) {
|
|
225
|
+
const stubPath = path.join(this.options.packageDir, stub.file);
|
|
226
|
+
if (!fs.existsSync(stubPath)) {
|
|
227
|
+
writes.push({ filePath: stubPath, content: stub.content });
|
|
228
|
+
continue;
|
|
229
|
+
}
|
|
230
|
+
|
|
231
|
+
const current = fs.readFileSync(stubPath, 'utf-8');
|
|
232
|
+
if (current.includes(targetNoExt)) {
|
|
233
|
+
continue;
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
if (this.options.fix) {
|
|
237
|
+
writes.push({ filePath: stubPath, content: stub.content });
|
|
238
|
+
} else {
|
|
239
|
+
conflicts.push({
|
|
240
|
+
file: stub.file,
|
|
241
|
+
found: `${stub.file} exists but does not point at ${targetNoExt}`,
|
|
242
|
+
expected: targetNoExt,
|
|
243
|
+
whyItMatters,
|
|
244
|
+
pasteBlock: stub.content.replace(/^/gm, ' ').trimRight(),
|
|
245
|
+
});
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
/**
|
|
251
|
+
* tsconfig: include the generated index; on subpath builds, exclude the root stubs from
|
|
252
|
+
* compilation. List membership only — nothing here can conflict.
|
|
253
|
+
*/
|
|
254
|
+
private applyTsconfig(writes: PendingWrite[]): void {
|
|
255
|
+
const tsconfigPath = path.join(this.options.packageDir, 'tsconfig.json');
|
|
256
|
+
const includePath = `./${path.relative(this.options.packageDir, this.options.generatedIndexPath)}`;
|
|
257
|
+
|
|
258
|
+
if (!fs.existsSync(tsconfigPath)) {
|
|
259
|
+
const tsconfig = Object.assign({}, tsconfigTemplate);
|
|
260
|
+
tsconfig.include = [includePath];
|
|
261
|
+
if (!this.isProd()) {
|
|
262
|
+
// Fresh exclude must carry node_modules: an explicit exclude replaces the default one.
|
|
263
|
+
tsconfig.exclude = ['node_modules', this.stubJsName(), this.stubDtsName()];
|
|
264
|
+
}
|
|
265
|
+
writes.push({ filePath: tsconfigPath, content: JSON.stringify(tsconfig, null, 4) });
|
|
266
|
+
return;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
const original = fs.readFileSync(tsconfigPath, 'utf-8');
|
|
270
|
+
const tsconfig = JSON.parse(original);
|
|
271
|
+
if (tsconfig.include && !tsconfig.include.includes(includePath)) {
|
|
272
|
+
tsconfig.include.push(includePath);
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
if (!this.isProd()) {
|
|
276
|
+
if (!Array.isArray(tsconfig.exclude)) {
|
|
277
|
+
tsconfig.exclude = ['node_modules'];
|
|
278
|
+
}
|
|
279
|
+
for (const stub of [this.stubJsName(), this.stubDtsName()]) {
|
|
280
|
+
if (!tsconfig.exclude.includes(stub)) {
|
|
281
|
+
tsconfig.exclude.push(stub);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
const updated = JSON.stringify(tsconfig, null, 4);
|
|
287
|
+
if (updated !== JSON.stringify(JSON.parse(original), null, 4)) {
|
|
288
|
+
writes.push({ filePath: tsconfigPath, content: updated });
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
/**
|
|
293
|
+
* Settle one string field: absent → set; equal → ok; a dangling value (points at a file
|
|
294
|
+
* that does not exist — npm init's boilerplate `"main": "index.js"`, a stale copy) → set;
|
|
295
|
+
* a value that points at a real, different file is a deliberate setting → conflict.
|
|
296
|
+
*/
|
|
297
|
+
private settleString(
|
|
298
|
+
packageJson: any,
|
|
299
|
+
field: string,
|
|
300
|
+
expected: string,
|
|
301
|
+
conflicts: Conflict[],
|
|
302
|
+
whyItMatters: string,
|
|
303
|
+
pasteBlock: string
|
|
304
|
+
): void {
|
|
305
|
+
const current = packageJson[field];
|
|
306
|
+
if (current === expected) {
|
|
307
|
+
return;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
if (typeof current === 'string' && fs.existsSync(path.join(this.options.packageDir, current))) {
|
|
311
|
+
if (this.options.fix) {
|
|
312
|
+
packageJson[field] = expected;
|
|
313
|
+
} else {
|
|
314
|
+
conflicts.push({
|
|
315
|
+
file: 'package.json',
|
|
316
|
+
found: `"${field}" is "${current}"`,
|
|
317
|
+
expected,
|
|
318
|
+
whyItMatters,
|
|
319
|
+
pasteBlock,
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
return;
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
packageJson[field] = expected;
|
|
326
|
+
}
|
|
327
|
+
|
|
328
|
+
private formatConflicts(conflicts: Conflict[]): string {
|
|
329
|
+
const lines: string[] = [];
|
|
330
|
+
lines.push(`reflection-build: this package's config conflicts with what the build produces.`);
|
|
331
|
+
lines.push(`Nothing was changed on disk.`);
|
|
332
|
+
lines.push('');
|
|
333
|
+
for (const conflict of conflicts) {
|
|
334
|
+
lines.push(`- ${conflict.file}: ${conflict.found}`);
|
|
335
|
+
lines.push(` the build's artifact is ${conflict.expected}`);
|
|
336
|
+
lines.push(` Why it matters: ${conflict.whyItMatters}`);
|
|
337
|
+
}
|
|
338
|
+
lines.push('');
|
|
339
|
+
lines.push(`The quickest fix — rewrite just these fields to match the build:`);
|
|
340
|
+
lines.push('');
|
|
341
|
+
lines.push(` npx reflection-build --fix`);
|
|
342
|
+
lines.push('');
|
|
343
|
+
lines.push(`Or paste the expected values in yourself:`);
|
|
344
|
+
lines.push('');
|
|
345
|
+
const seen: { [block: string]: boolean } = {};
|
|
346
|
+
for (const conflict of conflicts) {
|
|
347
|
+
if (seen[conflict.pasteBlock]) {
|
|
348
|
+
continue;
|
|
349
|
+
}
|
|
350
|
+
seen[conflict.pasteBlock] = true;
|
|
351
|
+
lines.push(conflict.pasteBlock);
|
|
352
|
+
lines.push('');
|
|
353
|
+
}
|
|
354
|
+
return lines.join('\n');
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
private isProd(): boolean {
|
|
358
|
+
return this.options.primaryRoot === 'src';
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
private prodIndexJs(): string {
|
|
362
|
+
return `./${path.join(this.options.distDirRel, 'generated', 'index.js')}`;
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
private prodIndexDts(): string {
|
|
366
|
+
return `./${path.join(this.options.distDirRel, 'generated', 'index.d.ts')}`;
|
|
367
|
+
}
|
|
368
|
+
|
|
369
|
+
private subpathIndexJs(): string {
|
|
370
|
+
return `./${path.join(this.options.distDirRel, 'generated', this.options.primaryRoot, 'index.js')}`;
|
|
371
|
+
}
|
|
372
|
+
|
|
373
|
+
private subpathIndexDts(): string {
|
|
374
|
+
return `./${path.join(this.options.distDirRel, 'generated', this.options.primaryRoot, 'index.d.ts')}`;
|
|
375
|
+
}
|
|
376
|
+
|
|
377
|
+
private stubJsName(): string {
|
|
378
|
+
return `${this.options.primaryRoot}.js`;
|
|
379
|
+
}
|
|
380
|
+
|
|
381
|
+
private stubDtsName(): string {
|
|
382
|
+
return `${this.options.primaryRoot}.d.ts`;
|
|
383
|
+
}
|
|
384
|
+
}
|