@proteinjs/reflection-build 2.0.2 → 2.1.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 +24 -0
- package/dist/src/ReflectionDoctor.d.ts +6 -1
- package/dist/src/ReflectionDoctor.js +53 -51
- package/dist/src/ReflectionDoctor.js.map +1 -1
- package/dist/src/codegen/CanonicalSourceGraph.d.ts +22 -0
- package/dist/src/codegen/CanonicalSourceGraph.js +60 -0
- package/dist/src/codegen/CanonicalSourceGraph.js.map +1 -0
- package/dist/src/codegen/writeGeneratedIndex.d.ts +12 -3
- package/dist/src/codegen/writeGeneratedIndex.js +21 -13
- package/dist/src/codegen/writeGeneratedIndex.js.map +1 -1
- package/dist/src/parser/PackageSourceFiles.d.ts +30 -0
- package/dist/src/parser/PackageSourceFiles.js +151 -0
- package/dist/src/parser/PackageSourceFiles.js.map +1 -0
- package/dist/src/parser/SharedQualifiedNames.d.ts +43 -0
- package/dist/src/parser/SharedQualifiedNames.js +81 -0
- package/dist/src/parser/SharedQualifiedNames.js.map +1 -0
- package/dist/src/parser/createGraphBuilder.d.ts +6 -1
- package/dist/src/parser/createGraphBuilder.js +16 -31
- package/dist/src/parser/createGraphBuilder.js.map +1 -1
- package/dist/src/parser/createSourceGraph.d.ts +6 -1
- package/dist/src/parser/createSourceGraph.js +23 -38
- package/dist/src/parser/createSourceGraph.js.map +1 -1
- package/dist/test/Class.test.js +1 -1
- package/dist/test/Class.test.js.map +1 -1
- package/dist/test/ExamplePackageFixture.d.ts +47 -0
- package/dist/test/ExamplePackageFixture.js +207 -0
- package/dist/test/ExamplePackageFixture.js.map +1 -0
- package/dist/test/GraphEmitRelativePaths.test.js +1 -1
- package/dist/test/GraphEmitRelativePaths.test.js.map +1 -1
- package/dist/test/PackageSourceFiles.test.d.ts +1 -0
- package/dist/test/PackageSourceFiles.test.js +57 -0
- package/dist/test/PackageSourceFiles.test.js.map +1 -0
- package/dist/test/ReflectionDoctor.test.js +55 -18
- package/dist/test/ReflectionDoctor.test.js.map +1 -1
- package/dist/test/ReproducibleGeneratedIndex.test.d.ts +1 -0
- package/dist/test/ReproducibleGeneratedIndex.test.js +224 -0
- package/dist/test/ReproducibleGeneratedIndex.test.js.map +1 -0
- package/dist/test/SharedQualifiedNames.test.d.ts +1 -0
- package/dist/test/SharedQualifiedNames.test.js +186 -0
- package/dist/test/SharedQualifiedNames.test.js.map +1 -0
- package/dist/test/examples/reproducible/index.d.ts +4 -0
- package/dist/test/examples/reproducible/index.js +21 -0
- package/dist/test/examples/reproducible/index.js.map +1 -0
- package/dist/test/examples/reproducible/src/alpha/Alpha.d.ts +10 -0
- package/dist/test/examples/reproducible/src/alpha/Alpha.js +18 -0
- package/dist/test/examples/reproducible/src/alpha/Alpha.js.map +1 -0
- package/dist/test/examples/reproducible/src/alpha/Defaults.d.ts +2 -0
- package/dist/test/examples/reproducible/src/alpha/Defaults.js +6 -0
- package/dist/test/examples/reproducible/src/alpha/Defaults.js.map +1 -0
- package/dist/test/examples/reproducible/src/beta/Beta.d.ts +5 -0
- package/dist/test/examples/reproducible/src/beta/Beta.js +12 -0
- package/dist/test/examples/reproducible/src/beta/Beta.js.map +1 -0
- package/dist/test/examples/reproducible/src/beta/deep/Gamma.d.ts +4 -0
- package/dist/test/examples/reproducible/src/beta/deep/Gamma.js +11 -0
- package/dist/test/examples/reproducible/src/beta/deep/Gamma.js.map +1 -0
- package/dist/test/examples/reproducible/src/zeta/Defaults.d.ts +2 -0
- package/dist/test/examples/reproducible/src/zeta/Defaults.js +6 -0
- package/dist/test/examples/reproducible/src/zeta/Defaults.js.map +1 -0
- package/dist/test/examples/reproducible/src/zeta/Zeta.d.ts +7 -0
- package/dist/test/examples/reproducible/src/zeta/Zeta.js +18 -0
- package/dist/test/examples/reproducible/src/zeta/Zeta.js.map +1 -0
- package/dist/test/examples/shared-name/index.d.ts +1 -0
- package/dist/test/examples/shared-name/index.js +18 -0
- package/dist/test/examples/shared-name/index.js.map +1 -0
- package/dist/test/examples/shared-name/src/alpha/Widget.d.ts +4 -0
- package/dist/test/examples/shared-name/src/alpha/Widget.js +3 -0
- package/dist/test/examples/shared-name/src/alpha/Widget.js.map +1 -0
- package/dist/test/examples/shared-name/src/beta/Widget.d.ts +3 -0
- package/dist/test/examples/shared-name/src/beta/Widget.js +13 -0
- package/dist/test/examples/shared-name/src/beta/Widget.js.map +1 -0
- package/dist/test/examples/shared-name/src/gamma/Gear.d.ts +7 -0
- package/dist/test/examples/shared-name/src/gamma/Gear.js +5 -0
- package/dist/test/examples/shared-name/src/gamma/Gear.js.map +1 -0
- package/dist/test/examples/shared-name-pruned/index.d.ts +1 -0
- package/dist/test/examples/shared-name-pruned/index.js +18 -0
- package/dist/test/examples/shared-name-pruned/index.js.map +1 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Limits.d.ts +3 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Limits.js +6 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Limits.js.map +1 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Shape.d.ts +3 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Shape.js +3 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Shape.js.map +1 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Tile.d.ts +7 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Tile.js +11 -0
- package/dist/test/examples/shared-name-pruned/src/alpha/Tile.js.map +1 -0
- package/dist/test/examples/shared-name-pruned/src/beta/Limits.d.ts +3 -0
- package/dist/test/examples/shared-name-pruned/src/beta/Limits.js +6 -0
- package/dist/test/examples/shared-name-pruned/src/beta/Limits.js.map +1 -0
- package/dist/test/examples/shared-name-pruned/src/beta/Shape.d.ts +3 -0
- package/dist/test/examples/shared-name-pruned/src/beta/Shape.js +3 -0
- package/dist/test/examples/shared-name-pruned/src/beta/Shape.js.map +1 -0
- package/dist/test/examples/source-repository/a/generated/index.js +28 -28
- package/dist/test/examples/source-repository/a/generated/index.js.map +1 -1
- package/dist/test/examples/source-repository/a/src/ExtendsForeignType.d.ts +1 -1
- package/dist/test/examples/source-repository/a/src/ExtendsForeignType.js +5 -5
- package/dist/test/examples/source-repository/a/src/ExtendsForeignType.js.map +1 -1
- package/dist/test/examples/source-repository/b/generated/index.js +3 -3
- package/dist/test/examples/source-repository/b/generated/index.js.map +1 -1
- package/package.json +5 -5
- package/src/ReflectionDoctor.ts +37 -17
- package/src/codegen/CanonicalSourceGraph.ts +50 -0
- package/src/codegen/writeGeneratedIndex.ts +35 -9
- package/src/parser/PackageSourceFiles.ts +66 -0
- package/src/parser/SharedQualifiedNames.ts +76 -0
- package/src/parser/createGraphBuilder.ts +25 -10
- package/src/parser/createSourceGraph.ts +14 -16
- package/test/Class.test.ts +1 -1
- package/test/ExamplePackageFixture.ts +142 -0
- package/test/GraphEmitRelativePaths.test.ts +1 -1
- package/test/PackageSourceFiles.test.ts +42 -0
- package/test/ReflectionDoctor.test.ts +50 -12
- package/test/ReproducibleGeneratedIndex.test.ts +114 -0
- package/test/SharedQualifiedNames.test.ts +89 -0
- package/test/examples/reproducible/index.ts +4 -0
- package/test/examples/reproducible/src/alpha/Alpha.ts +13 -0
- package/test/examples/reproducible/src/alpha/Defaults.ts +4 -0
- package/test/examples/reproducible/src/beta/Beta.ts +7 -0
- package/test/examples/reproducible/src/beta/deep/Gamma.ts +5 -0
- package/test/examples/reproducible/src/zeta/Defaults.ts +4 -0
- package/test/examples/reproducible/src/zeta/Zeta.ts +9 -0
- package/test/examples/shared-name/index.ts +1 -0
- package/test/examples/shared-name/src/alpha/Widget.ts +6 -0
- package/test/examples/shared-name/src/beta/Widget.ts +5 -0
- package/test/examples/shared-name/src/gamma/Gear.ts +9 -0
- package/test/examples/shared-name-pruned/index.ts +1 -0
- package/test/examples/shared-name-pruned/src/alpha/Limits.ts +2 -0
- package/test/examples/shared-name-pruned/src/alpha/Shape.ts +3 -0
- package/test/examples/shared-name-pruned/src/alpha/Tile.ts +9 -0
- package/test/examples/shared-name-pruned/src/beta/Limits.ts +2 -0
- package/test/examples/shared-name-pruned/src/beta/Shape.ts +4 -0
- package/test/examples/source-repository/a/dist/codegen/CanonicalSourceGraph.d.ts +22 -0
- package/test/examples/source-repository/a/dist/codegen/CanonicalSourceGraph.js +60 -0
- package/test/examples/source-repository/a/dist/codegen/CanonicalSourceGraph.js.map +1 -0
- package/test/examples/source-repository/a/dist/codegen/writeGeneratedIndex.d.ts +12 -3
- package/test/examples/source-repository/a/dist/codegen/writeGeneratedIndex.js +21 -13
- package/test/examples/source-repository/a/dist/codegen/writeGeneratedIndex.js.map +1 -1
- package/test/examples/source-repository/a/dist/generated/index.js +28 -28
- package/test/examples/source-repository/a/dist/generated/index.js.map +1 -1
- package/test/examples/source-repository/a/dist/parser/PackageSourceFiles.d.ts +30 -0
- package/test/examples/source-repository/a/dist/parser/PackageSourceFiles.js +151 -0
- package/test/examples/source-repository/a/dist/parser/PackageSourceFiles.js.map +1 -0
- package/test/examples/source-repository/a/dist/parser/SharedQualifiedNames.d.ts +43 -0
- package/test/examples/source-repository/a/dist/parser/SharedQualifiedNames.js +81 -0
- package/test/examples/source-repository/a/dist/parser/SharedQualifiedNames.js.map +1 -0
- package/test/examples/source-repository/a/dist/parser/createGraphBuilder.d.ts +6 -1
- package/test/examples/source-repository/a/dist/parser/createGraphBuilder.js +16 -31
- package/test/examples/source-repository/a/dist/parser/createGraphBuilder.js.map +1 -1
- package/test/examples/source-repository/a/dist/parser/createSourceGraph.d.ts +6 -1
- package/test/examples/source-repository/a/dist/parser/createSourceGraph.js +23 -38
- package/test/examples/source-repository/a/dist/parser/createSourceGraph.js.map +1 -1
- package/test/examples/source-repository/a/dist/src/ExtendsForeignType.d.ts +1 -1
- package/test/examples/source-repository/a/dist/src/ExtendsForeignType.js +5 -5
- package/test/examples/source-repository/a/dist/src/ExtendsForeignType.js.map +1 -1
- package/test/examples/source-repository/a/generated/index.ts +44 -44
- package/test/examples/source-repository/a/node_modules/.package-lock.json +13 -13
- package/test/examples/source-repository/a/node_modules/@proteinjs/util/CHANGELOG.md +8 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util/LICENSE +21 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util/package.json +3 -2
- package/test/examples/source-repository/a/node_modules/@proteinjs/util/tsconfig.json +1 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/CHANGELOG.md +63 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/LICENSE +21 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/index.d.ts +1 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/index.js +1 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/index.js.map +1 -1
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/ArgsMap.js +7 -4
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/ArgsMap.js.map +1 -1
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/Fs.js +16 -12
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/Fs.js.map +1 -1
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/PackageUtil.d.ts +59 -13
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/PackageUtil.js +119 -80
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/PackageUtil.js.map +1 -1
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/WorkspaceDeclaration.d.ts +91 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/WorkspaceDeclaration.js +608 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/WorkspaceDeclaration.js.map +1 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/dist/src/promisifiedFs.d.ts +0 -1
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/index.ts +1 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/package.json +4 -3
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/src/ArgsMap.ts +7 -4
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/src/Fs.ts +12 -9
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/src/PackageUtil.ts +92 -38
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/src/WorkspaceDeclaration.ts +340 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/test/ArgsMap.test.ts +35 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/test/Fs.getFilePathsMatchingGlob.test.ts +70 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/test/PackageUtil.getLocalPackageMap.test.ts +107 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/test/PackageUtil.pathKeyedIdentity.test.ts +169 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/test/WorkspaceDeclaration.test.ts +174 -0
- package/test/examples/source-repository/a/node_modules/@proteinjs/util-node/tsconfig.json +1 -0
- package/test/examples/source-repository/a/package-lock.json +16 -16
- package/test/examples/source-repository/a/package.json +3 -3
- package/test/examples/source-repository/a/src/ExtendsForeignType.ts +1 -1
- package/test/examples/source-repository/b/dist/codegen/CanonicalSourceGraph.d.ts +22 -0
- package/test/examples/source-repository/b/dist/codegen/CanonicalSourceGraph.js +60 -0
- package/test/examples/source-repository/b/dist/codegen/CanonicalSourceGraph.js.map +1 -0
- package/test/examples/source-repository/b/dist/codegen/writeGeneratedIndex.d.ts +12 -3
- package/test/examples/source-repository/b/dist/codegen/writeGeneratedIndex.js +21 -13
- package/test/examples/source-repository/b/dist/codegen/writeGeneratedIndex.js.map +1 -1
- package/test/examples/source-repository/b/dist/generated/index.js +3 -3
- package/test/examples/source-repository/b/dist/generated/index.js.map +1 -1
- package/test/examples/source-repository/b/dist/parser/PackageSourceFiles.d.ts +30 -0
- package/test/examples/source-repository/b/dist/parser/PackageSourceFiles.js +151 -0
- package/test/examples/source-repository/b/dist/parser/PackageSourceFiles.js.map +1 -0
- package/test/examples/source-repository/b/dist/parser/SharedQualifiedNames.d.ts +43 -0
- package/test/examples/source-repository/b/dist/parser/SharedQualifiedNames.js +81 -0
- package/test/examples/source-repository/b/dist/parser/SharedQualifiedNames.js.map +1 -0
- package/test/examples/source-repository/b/dist/parser/createGraphBuilder.d.ts +6 -1
- package/test/examples/source-repository/b/dist/parser/createGraphBuilder.js +16 -31
- package/test/examples/source-repository/b/dist/parser/createGraphBuilder.js.map +1 -1
- package/test/examples/source-repository/b/dist/parser/createSourceGraph.d.ts +6 -1
- package/test/examples/source-repository/b/dist/parser/createSourceGraph.js +23 -38
- package/test/examples/source-repository/b/dist/parser/createSourceGraph.js.map +1 -1
- package/test/examples/source-repository/b/generated/index.ts +6 -6
- package/test/examples/source-repository/b/node_modules/.package-lock.json +10 -10
- package/test/examples/source-repository/b/node_modules/@proteinjs/util/CHANGELOG.md +8 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util/LICENSE +21 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util/package.json +3 -2
- package/test/examples/source-repository/b/node_modules/@proteinjs/util/tsconfig.json +1 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/CHANGELOG.md +63 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/LICENSE +21 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/index.d.ts +1 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/index.js +1 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/index.js.map +1 -1
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/ArgsMap.js +7 -4
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/ArgsMap.js.map +1 -1
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/Fs.js +16 -12
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/Fs.js.map +1 -1
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/PackageUtil.d.ts +59 -13
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/PackageUtil.js +119 -80
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/PackageUtil.js.map +1 -1
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/WorkspaceDeclaration.d.ts +91 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/WorkspaceDeclaration.js +608 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/WorkspaceDeclaration.js.map +1 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/dist/src/promisifiedFs.d.ts +0 -1
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/index.ts +1 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/package.json +4 -3
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/src/ArgsMap.ts +7 -4
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/src/Fs.ts +12 -9
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/src/PackageUtil.ts +92 -38
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/src/WorkspaceDeclaration.ts +340 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/test/ArgsMap.test.ts +35 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/test/Fs.getFilePathsMatchingGlob.test.ts +70 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/test/PackageUtil.getLocalPackageMap.test.ts +107 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/test/PackageUtil.pathKeyedIdentity.test.ts +169 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/test/WorkspaceDeclaration.test.ts +174 -0
- package/test/examples/source-repository/b/node_modules/@proteinjs/util-node/tsconfig.json +1 -0
- package/test/examples/source-repository/b/package-lock.json +13 -13
- package/test/examples/source-repository/b/package.json +3 -3
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import * as path from 'path';
|
|
2
1
|
import * as graphlib from '@dagrejs/graphlib';
|
|
3
2
|
import {
|
|
4
3
|
VariableDeclaration as ParserVariableDeclaration,
|
|
@@ -14,14 +13,30 @@ import { PackageNameFinder } from './types/PackageNameFinder';
|
|
|
14
13
|
import { createTypeAliasDeclaration } from './types/createTypeAliasDeclaration';
|
|
15
14
|
import { createClassDeclaration } from './types/createClassDeclaration';
|
|
16
15
|
import { createInterfaceDeclaration } from './types/createInterfaceDeclaration';
|
|
16
|
+
import { PackageSourceFiles } from './PackageSourceFiles';
|
|
17
|
+
import { SharedQualifiedNames } from './SharedQualifiedNames';
|
|
17
18
|
|
|
18
|
-
|
|
19
|
+
/**
|
|
20
|
+
* @param sharedNames records the file of every declaration set on the graph: the graph keeps one
|
|
21
|
+
* node per qualified name, so it cannot show a name two files declare
|
|
22
|
+
*/
|
|
23
|
+
export function createGraphBuilder(
|
|
24
|
+
graph: graphlib.Graph,
|
|
25
|
+
packageJson: any,
|
|
26
|
+
packageJsonDir: string,
|
|
27
|
+
sharedNames: SharedQualifiedNames
|
|
28
|
+
) {
|
|
19
29
|
const packageName = packageJson.name;
|
|
20
30
|
return async (parsedFile: File): Promise<void> => {
|
|
21
|
-
// Package-relative: the graph is a serialized, shipped artifact —
|
|
22
|
-
// paths (CI runner paths, local checkout paths)
|
|
23
|
-
//
|
|
24
|
-
|
|
31
|
+
// Package-relative with `/` separators: the graph is a serialized, shipped artifact —
|
|
32
|
+
// absolute build-machine paths (CI runner paths, local checkout paths) and the platform's
|
|
33
|
+
// separator must never enter it. Build-time consumers that need a real location
|
|
34
|
+
// (sourceLink imports) join this with the package dir.
|
|
35
|
+
const filePath = PackageSourceFiles.relativePath(packageJsonDir, parsedFile.filePath);
|
|
36
|
+
const setDeclaration = (qualifiedName: string, value: object) => {
|
|
37
|
+
graph.setNode(qualifiedName, value);
|
|
38
|
+
sharedNames.record(qualifiedName, filePath);
|
|
39
|
+
};
|
|
25
40
|
for (const declaration of parsedFile.declarations) {
|
|
26
41
|
if (!(declaration as any)['isExported']) {
|
|
27
42
|
continue;
|
|
@@ -38,7 +53,7 @@ export function createGraphBuilder(graph: graphlib.Graph, packageJson: any, pack
|
|
|
38
53
|
packageNameFinder,
|
|
39
54
|
filePath
|
|
40
55
|
);
|
|
41
|
-
|
|
56
|
+
setDeclaration(
|
|
42
57
|
variableDeclaration.qualifiedName,
|
|
43
58
|
Object.assign(variableDeclaration, { sourceType: SourceType.variable })
|
|
44
59
|
);
|
|
@@ -54,7 +69,7 @@ export function createGraphBuilder(graph: graphlib.Graph, packageJson: any, pack
|
|
|
54
69
|
packageNameFinder,
|
|
55
70
|
filePath
|
|
56
71
|
);
|
|
57
|
-
|
|
72
|
+
setDeclaration(
|
|
58
73
|
typeAliasDeclaration.qualifiedName,
|
|
59
74
|
Object.assign(typeAliasDeclaration, { sourceType: SourceType.typeAlias })
|
|
60
75
|
);
|
|
@@ -70,7 +85,7 @@ export function createGraphBuilder(graph: graphlib.Graph, packageJson: any, pack
|
|
|
70
85
|
packageNameFinder,
|
|
71
86
|
filePath
|
|
72
87
|
);
|
|
73
|
-
|
|
88
|
+
setDeclaration(
|
|
74
89
|
classDeclaration.qualifiedName,
|
|
75
90
|
Object.assign(classDeclaration, { sourceType: SourceType.class })
|
|
76
91
|
);
|
|
@@ -107,7 +122,7 @@ export function createGraphBuilder(graph: graphlib.Graph, packageJson: any, pack
|
|
|
107
122
|
packageNameFinder,
|
|
108
123
|
filePath
|
|
109
124
|
);
|
|
110
|
-
|
|
125
|
+
setDeclaration(
|
|
111
126
|
interfaceDeclaration.qualifiedName,
|
|
112
127
|
Object.assign(interfaceDeclaration, { sourceType: SourceType.interface })
|
|
113
128
|
);
|
|
@@ -1,14 +1,20 @@
|
|
|
1
1
|
import * as graphlib from '@dagrejs/graphlib';
|
|
2
|
-
import globby from 'globby';
|
|
3
2
|
import * as path from 'path';
|
|
4
3
|
import { TypescriptParser } from '../../modules/typescript-parser';
|
|
5
4
|
import { promisifiedFs } from '@proteinjs/util-node';
|
|
6
5
|
import { createGraphBuilder } from './createGraphBuilder';
|
|
6
|
+
import { PackageSourceFiles } from './PackageSourceFiles';
|
|
7
|
+
import { SharedQualifiedNames } from './SharedQualifiedNames';
|
|
7
8
|
|
|
9
|
+
/**
|
|
10
|
+
* @param sharedNames receives the file of every declaration the graph holds a node for; the
|
|
11
|
+
* build reads the names two files declare from it after the prune
|
|
12
|
+
*/
|
|
8
13
|
export async function createSourceGraph(
|
|
9
14
|
dir: string,
|
|
10
15
|
excludedDirs: string[] = [],
|
|
11
|
-
sourceRootsRel: string | string[] = 'src'
|
|
16
|
+
sourceRootsRel: string | string[] = 'src',
|
|
17
|
+
sharedNames: SharedQualifiedNames = new SharedQualifiedNames()
|
|
12
18
|
) {
|
|
13
19
|
const packageJsonPath = path.join(dir, 'package.json');
|
|
14
20
|
if (!(await promisifiedFs.exists(packageJsonPath))) {
|
|
@@ -17,24 +23,16 @@ export async function createSourceGraph(
|
|
|
17
23
|
|
|
18
24
|
const packageJson = require(packageJsonPath);
|
|
19
25
|
const roots = Array.isArray(sourceRootsRel) ? sourceRootsRel : [sourceRootsRel];
|
|
20
|
-
|
|
21
|
-
const patterns: string[] = [];
|
|
22
|
-
for (const rel of roots) {
|
|
23
|
-
const root = path.join(dir, rel);
|
|
24
|
-
patterns.push(path.join(root, '**/*.ts'));
|
|
25
|
-
patterns.push(path.join(root, '**/*.tsx'));
|
|
26
|
-
}
|
|
27
|
-
|
|
28
|
-
const excludePatterns = ['!**/node_modules/**', '!**/generated/**', ...excludedDirs.map((d) => `!${d}`)];
|
|
29
|
-
|
|
30
|
-
const sourceFilePaths = await globby([...patterns, ...excludePatterns]);
|
|
31
|
-
const uniqueFilePaths = Array.from(new Set(sourceFilePaths));
|
|
26
|
+
const sourceFilePaths = await new PackageSourceFiles(dir, roots, excludedDirs).list();
|
|
32
27
|
|
|
33
28
|
const graph = new graphlib.Graph();
|
|
34
|
-
const addSourceFile = createGraphBuilder(graph, packageJson, dir);
|
|
29
|
+
const addSourceFile = createGraphBuilder(graph, packageJson, dir, sharedNames);
|
|
35
30
|
const parser = new TypescriptParser();
|
|
36
31
|
|
|
37
|
-
|
|
32
|
+
// Sequential, in PackageSourceFiles order: the parse order is part of the graph's content (a
|
|
33
|
+
// name exported by two files holds the declaration parsed last — the build refuses such a name
|
|
34
|
+
// when it survives the prune) and the order `sharedNames` lists the declaring files in.
|
|
35
|
+
for (const sourceFilePath of sourceFilePaths) {
|
|
38
36
|
const sourceFile = await parser.parseFile(sourceFilePath, path.dirname(sourceFilePath));
|
|
39
37
|
await addSourceFile(sourceFile);
|
|
40
38
|
}
|
package/test/Class.test.ts
CHANGED
|
@@ -60,7 +60,7 @@ test('do not load non-loadable classes', async () => {
|
|
|
60
60
|
).toBeUndefined();
|
|
61
61
|
expect(
|
|
62
62
|
sourceRepository.flattenedSourceGraph.classes[
|
|
63
|
-
'@proteinjs/reflection-build-test-a/
|
|
63
|
+
'@proteinjs/reflection-build-test-a/ImplementsExtendsNotLoadableForeignInterface'
|
|
64
64
|
]
|
|
65
65
|
).toBeUndefined();
|
|
66
66
|
expect(
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import * as crypto from 'crypto';
|
|
2
|
+
import * as fs from 'fs';
|
|
3
|
+
import * as os from 'os';
|
|
4
|
+
import * as path from 'path';
|
|
5
|
+
import { execFileSync } from 'child_process';
|
|
6
|
+
import { build } from '../src/build';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Disposable copies of an example package under `test/examples`, each at its own absolute
|
|
10
|
+
* location, built the way the `reflection-build` bin builds a package (the package dir arrives
|
|
11
|
+
* as INIT_CWD), compiled with tsc and packed with `npm pack` — so a test can compare what two
|
|
12
|
+
* builds of the same sources ship, or what one build refuses.
|
|
13
|
+
*/
|
|
14
|
+
export class ExamplePackageFixture {
|
|
15
|
+
private static readonly NODE_MODULES = path.join(__dirname, '..', 'node_modules');
|
|
16
|
+
private readonly sources: string;
|
|
17
|
+
private readonly tmpRoot: string;
|
|
18
|
+
private readonly links: string[] = [];
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* @param example the example's directory under `test/examples`
|
|
22
|
+
* @param packageName the name its copies are built under (a fixture name, never a published package's)
|
|
23
|
+
*/
|
|
24
|
+
constructor(
|
|
25
|
+
example: string,
|
|
26
|
+
readonly packageName: string
|
|
27
|
+
) {
|
|
28
|
+
this.sources = path.join(__dirname, 'examples', example);
|
|
29
|
+
this.tmpRoot = fs.mkdtempSync(path.join(os.tmpdir(), `refl-${example}-`));
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/** `packageName/name`: the qualified name this package declares `name` under. */
|
|
33
|
+
qualified(name: string): string {
|
|
34
|
+
return `${this.packageName}/${name}`;
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
/** Copies the example package to `<tmp>/<location>` and returns its absolute path. */
|
|
38
|
+
materialize(location: string): string {
|
|
39
|
+
const packageDir = path.join(this.tmpRoot, location);
|
|
40
|
+
fs.mkdirSync(path.dirname(packageDir), { recursive: true });
|
|
41
|
+
this.copyTree(this.sources, packageDir);
|
|
42
|
+
fs.writeFileSync(path.join(packageDir, 'package.json'), JSON.stringify(this.packageJson(), null, 2));
|
|
43
|
+
fs.writeFileSync(path.join(packageDir, 'tsconfig.json'), JSON.stringify(this.tsconfig(), null, 2));
|
|
44
|
+
const link = path.join(packageDir, 'node_modules');
|
|
45
|
+
fs.symlinkSync(ExamplePackageFixture.NODE_MODULES, link, 'dir');
|
|
46
|
+
this.links.push(link);
|
|
47
|
+
return packageDir;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
/** Runs reflection-build in `packageDir`; returns the generated index it wrote (rejects when the build refuses). */
|
|
51
|
+
async generate(packageDir: string): Promise<string> {
|
|
52
|
+
const initCwd = process.env.INIT_CWD;
|
|
53
|
+
process.env.INIT_CWD = packageDir;
|
|
54
|
+
try {
|
|
55
|
+
await build();
|
|
56
|
+
} finally {
|
|
57
|
+
if (initCwd === undefined) {
|
|
58
|
+
delete process.env.INIT_CWD;
|
|
59
|
+
} else {
|
|
60
|
+
process.env.INIT_CWD = initCwd;
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
return fs.readFileSync(path.join(packageDir, 'generated', 'index.ts'), 'utf-8');
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** tsc over the package (its tsconfig includes the generated index). */
|
|
67
|
+
compile(packageDir: string): void {
|
|
68
|
+
execFileSync(process.execPath, [require.resolve('typescript/bin/tsc'), '-p', packageDir], { stdio: 'pipe' });
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/** `npm pack` of the package; returns the tarball's sha256. */
|
|
72
|
+
packSha256(packageDir: string): string {
|
|
73
|
+
const destination = fs.mkdtempSync(path.join(this.tmpRoot, 'pack-'));
|
|
74
|
+
execFileSync('npm', ['pack', '--pack-destination', destination], { cwd: packageDir, stdio: 'pipe' });
|
|
75
|
+
const [tarball] = fs.readdirSync(destination);
|
|
76
|
+
return this.sha256(fs.readFileSync(path.join(destination, tarball)));
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
sha256(bytes: Buffer | string): string {
|
|
80
|
+
return crypto.createHash('sha256').update(bytes).digest('hex');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/** The source graph a generated index embeds, read the way the runtime reads it. */
|
|
84
|
+
graphOf(generatedIndex: string): { nodes: { v: string; value?: any }[]; edges: { v: string; w: string }[] } {
|
|
85
|
+
const literal = generatedIndex.match(/const sourceGraph = (".*");/);
|
|
86
|
+
if (!literal) {
|
|
87
|
+
throw new Error('no serialized sourceGraph in the generated index');
|
|
88
|
+
}
|
|
89
|
+
return JSON.parse(JSON.parse(literal[1]).replace(/\\'/g, "'"));
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
/** Removes the copies; each linked node_modules is unlinked first, never descended into. */
|
|
93
|
+
dispose(): void {
|
|
94
|
+
for (const link of this.links) {
|
|
95
|
+
fs.unlinkSync(link);
|
|
96
|
+
}
|
|
97
|
+
execFileSync('rm', ['-rf', this.tmpRoot]);
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
private packageJson(): object {
|
|
101
|
+
return {
|
|
102
|
+
name: this.packageName,
|
|
103
|
+
version: '0.0.1',
|
|
104
|
+
private: true,
|
|
105
|
+
main: './dist/generated/index.js',
|
|
106
|
+
types: './dist/generated/index.d.ts',
|
|
107
|
+
files: ['dist/**'],
|
|
108
|
+
dependencies: { '@proteinjs/reflection': '^1.2.1' },
|
|
109
|
+
};
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
private tsconfig(): object {
|
|
113
|
+
return {
|
|
114
|
+
compilerOptions: {
|
|
115
|
+
noEmitOnError: true,
|
|
116
|
+
target: 'es5',
|
|
117
|
+
module: 'commonjs',
|
|
118
|
+
declaration: true,
|
|
119
|
+
sourceMap: true,
|
|
120
|
+
outDir: './dist/',
|
|
121
|
+
strict: true,
|
|
122
|
+
esModuleInterop: true,
|
|
123
|
+
skipLibCheck: true,
|
|
124
|
+
types: [],
|
|
125
|
+
},
|
|
126
|
+
include: ['./index.ts', './generated/index.ts'],
|
|
127
|
+
};
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
private copyTree(from: string, to: string): void {
|
|
131
|
+
fs.mkdirSync(to, { recursive: true });
|
|
132
|
+
for (const entry of fs.readdirSync(from, { withFileTypes: true })) {
|
|
133
|
+
const source = path.join(from, entry.name);
|
|
134
|
+
const target = path.join(to, entry.name);
|
|
135
|
+
if (entry.isDirectory()) {
|
|
136
|
+
this.copyTree(source, target);
|
|
137
|
+
} else {
|
|
138
|
+
fs.copyFileSync(source, target);
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
}
|
|
@@ -78,7 +78,7 @@ describe('graph emit: package-relative filePaths', () => {
|
|
|
78
78
|
}
|
|
79
79
|
// Fixture a declares sources under src/ — the graph must actually carry them.
|
|
80
80
|
expect(checked.length).toBeGreaterThan(0);
|
|
81
|
-
expect(checked.every((p) => p.startsWith('src'
|
|
81
|
+
expect(checked.every((p) => p.startsWith('src/') || p.startsWith('index.'))).toBe(true);
|
|
82
82
|
});
|
|
83
83
|
|
|
84
84
|
it('sourceLink imports still resolve to the linked source files (outcome: links survive)', () => {
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import * as path from 'path';
|
|
2
|
+
import { PackageSourceFiles } from '../src/parser/PackageSourceFiles';
|
|
3
|
+
|
|
4
|
+
/**
|
|
5
|
+
* The two machine-specific inputs `PackageSourceFiles` closes where they enter the generated
|
|
6
|
+
* index: the platform's path separator and the machine's collation. The Windows half cannot run
|
|
7
|
+
* on the machine that runs this suite, so the class is loaded once more against `path.win32` in
|
|
8
|
+
* an isolated module registry — the same code, Windows paths in.
|
|
9
|
+
*/
|
|
10
|
+
describe('PackageSourceFiles', () => {
|
|
11
|
+
test('relativePath writes / separators and no leading ./ from Windows paths', () => {
|
|
12
|
+
jest.isolateModules(() => {
|
|
13
|
+
jest.doMock('path', () => path.win32);
|
|
14
|
+
const { PackageSourceFiles: OnWindows } = require('../src/parser/PackageSourceFiles');
|
|
15
|
+
expect(OnWindows.relativePath('C:\\work\\pkg', 'C:\\work\\pkg\\src\\beta\\deep\\Gamma.ts')).toBe(
|
|
16
|
+
'src/beta/deep/Gamma.ts'
|
|
17
|
+
);
|
|
18
|
+
expect(OnWindows.relativePath('C:\\work\\pkg\\generated', 'C:\\work\\pkg\\src\\alpha\\Alpha.ts')).toBe(
|
|
19
|
+
'../src/alpha/Alpha.ts'
|
|
20
|
+
);
|
|
21
|
+
expect(OnWindows.relativePath('C:\\work\\pkg\\generated', 'C:\\work\\pkg')).toBe('..');
|
|
22
|
+
});
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
test('relativePath writes the same form from POSIX paths', () => {
|
|
26
|
+
expect(PackageSourceFiles.relativePath('/work/pkg', '/work/pkg/src/beta/deep/Gamma.ts')).toBe(
|
|
27
|
+
'src/beta/deep/Gamma.ts'
|
|
28
|
+
);
|
|
29
|
+
expect(PackageSourceFiles.relativePath('/work/pkg/generated', '/work/pkg/src/alpha/Alpha.ts')).toBe(
|
|
30
|
+
'../src/alpha/Alpha.ts'
|
|
31
|
+
);
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('compare orders by code unit, so upper case sorts before lower case on every machine', () => {
|
|
35
|
+
// 'Z' is 0x5a, 'd' is 0x64: code-unit order puts ZetaPlugin first; a locale collation would not.
|
|
36
|
+
expect(PackageSourceFiles.compare('src/zeta/ZetaPlugin.ts', 'src/zeta/defaults.ts')).toBeLessThan(0);
|
|
37
|
+
expect(PackageSourceFiles.compare('src/zeta/defaults.ts', 'src/zeta/ZetaPlugin.ts')).toBeGreaterThan(0);
|
|
38
|
+
expect(PackageSourceFiles.compare('src/a.ts', 'src/a.ts')).toBe(0);
|
|
39
|
+
// A shorter path sorts before its extension: 'SourceRecord.ts' before 'SourceRecordLoader.ts' ('.' is 0x2e, 'L' is 0x4c).
|
|
40
|
+
expect(PackageSourceFiles.compare('src/SourceRecord.ts', 'src/SourceRecordLoader.ts')).toBeLessThan(0);
|
|
41
|
+
});
|
|
42
|
+
});
|
|
@@ -16,6 +16,18 @@ import { writeGeneratedIndex } from '../src/codegen/writeGeneratedIndex';
|
|
|
16
16
|
describe('ReflectionDoctor', () => {
|
|
17
17
|
const fixtureA = path.join(__dirname, 'examples', 'source-repository', 'a');
|
|
18
18
|
|
|
19
|
+
const emitFixture = async (pkgDirToBuild: string) => {
|
|
20
|
+
const generatedDir = path.join(pkgDirToBuild, 'generated');
|
|
21
|
+
fs.mkdirSync(generatedDir, { recursive: true });
|
|
22
|
+
const generatedIndexPath = path.join(generatedDir, 'index.ts');
|
|
23
|
+
await writeGeneratedIndex(pkgDirToBuild, generatedDir, generatedIndexPath, ['src']);
|
|
24
|
+
// The doctor reads the built artifact; the sourceGraph/sourceLinks lines it parses are
|
|
25
|
+
// identical between generated/index.ts and its compiled dist twin.
|
|
26
|
+
const distGeneratedDir = path.join(pkgDirToBuild, 'dist', 'generated');
|
|
27
|
+
fs.mkdirSync(distGeneratedDir, { recursive: true });
|
|
28
|
+
fs.copyFileSync(generatedIndexPath, path.join(distGeneratedDir, 'index.js'));
|
|
29
|
+
};
|
|
30
|
+
|
|
19
31
|
describe('inventory + healthy fixture (fixture a)', () => {
|
|
20
32
|
it('reports dependency graphs, own graph size, and no orphans', async () => {
|
|
21
33
|
const doctor = new ReflectionDoctor(fixtureA);
|
|
@@ -61,18 +73,6 @@ describe('ReflectionDoctor', () => {
|
|
|
61
73
|
describe('broken chains (crafted fixture)', () => {
|
|
62
74
|
let pkgDir: string;
|
|
63
75
|
|
|
64
|
-
const emitFixture = async (pkgDirToBuild: string) => {
|
|
65
|
-
const generatedDir = path.join(pkgDirToBuild, 'generated');
|
|
66
|
-
fs.mkdirSync(generatedDir, { recursive: true });
|
|
67
|
-
const generatedIndexPath = path.join(generatedDir, 'index.ts');
|
|
68
|
-
await writeGeneratedIndex(pkgDirToBuild, generatedDir, generatedIndexPath, ['src']);
|
|
69
|
-
// The doctor reads the built artifact; the sourceGraph/sourceLinks lines it parses are
|
|
70
|
-
// identical between generated/index.ts and its compiled dist twin.
|
|
71
|
-
const distGeneratedDir = path.join(pkgDirToBuild, 'dist', 'generated');
|
|
72
|
-
fs.mkdirSync(distGeneratedDir, { recursive: true });
|
|
73
|
-
fs.copyFileSync(generatedIndexPath, path.join(distGeneratedDir, 'index.js'));
|
|
74
|
-
};
|
|
75
|
-
|
|
76
76
|
beforeEach(() => {
|
|
77
77
|
pkgDir = fs.mkdtempSync(path.join(os.tmpdir(), 'refl-doctor-'));
|
|
78
78
|
fs.writeFileSync(
|
|
@@ -190,4 +190,42 @@ describe('ReflectionDoctor', () => {
|
|
|
190
190
|
expect(explanation.verdict).toContain('rebuild');
|
|
191
191
|
});
|
|
192
192
|
});
|
|
193
|
+
|
|
194
|
+
describe('a name two files declare (crafted fixture)', () => {
|
|
195
|
+
let pkgDir: string;
|
|
196
|
+
|
|
197
|
+
beforeEach(() => {
|
|
198
|
+
pkgDir = fs.mkdtempSync(path.join(os.tmpdir(), 'refl-doctor-shared-'));
|
|
199
|
+
fs.writeFileSync(
|
|
200
|
+
path.join(pkgDir, 'package.json'),
|
|
201
|
+
JSON.stringify({ name: '@test/doctor-shared-name', version: '1.0.0', dependencies: {} }, null, 2)
|
|
202
|
+
);
|
|
203
|
+
fs.mkdirSync(path.join(pkgDir, 'src', 'alpha'), { recursive: true });
|
|
204
|
+
fs.writeFileSync(path.join(pkgDir, 'src', 'index.ts'), `export * from './alpha/Widget';\n`);
|
|
205
|
+
fs.writeFileSync(
|
|
206
|
+
path.join(pkgDir, 'src', 'alpha', 'Widget.ts'),
|
|
207
|
+
`import { Loadable } from '@proteinjs/reflection';\n\nexport interface Widget extends Loadable {}\n`
|
|
208
|
+
);
|
|
209
|
+
});
|
|
210
|
+
|
|
211
|
+
afterEach(() => {
|
|
212
|
+
fs.rmdirSync(pkgDir, { recursive: true });
|
|
213
|
+
});
|
|
214
|
+
|
|
215
|
+
it('reports a name that reaches the graph from two files, with every declaring file, as the build would refuse it', async () => {
|
|
216
|
+
// Built while one file declared the name; a second file declares it since.
|
|
217
|
+
await emitFixture(pkgDir);
|
|
218
|
+
fs.mkdirSync(path.join(pkgDir, 'src', 'beta'));
|
|
219
|
+
fs.writeFileSync(path.join(pkgDir, 'src', 'beta', 'Widget.ts'), `export class Widget {}\n`);
|
|
220
|
+
|
|
221
|
+
const report = await new ReflectionDoctor(pkgDir).diagnose();
|
|
222
|
+
expect(report.sharedNames).toEqual([
|
|
223
|
+
{ qualifiedName: '@test/doctor-shared-name/Widget', filePaths: ['src/alpha/Widget.ts', 'src/beta/Widget.ts'] },
|
|
224
|
+
]);
|
|
225
|
+
expect(report.healthy).toBe(false);
|
|
226
|
+
expect(report.text).toContain(
|
|
227
|
+
' @test/doctor-shared-name/Widget\n declared in src/alpha/Widget.ts\n declared in src/beta/Widget.ts'
|
|
228
|
+
);
|
|
229
|
+
});
|
|
230
|
+
});
|
|
193
231
|
});
|
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { ExamplePackageFixture } from './ExamplePackageFixture';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* The same sources build to the same bytes: the generated index, the compiled dist and the
|
|
7
|
+
* packed tarball do not depend on where the package is checked out, nor on the order the
|
|
8
|
+
* filesystem hands the source files back (which varies by filesystem and from run to run).
|
|
9
|
+
*
|
|
10
|
+
* The enumeration is stubbed at the glob the build reads its sources through: the real glob's
|
|
11
|
+
* result in a fixed base order (code-unit sorted — the glob's own order varies from copy to copy,
|
|
12
|
+
* so a reversal of it would not be the same reversal twice), reordered by `mockEnumerationOrder`.
|
|
13
|
+
*/
|
|
14
|
+
let mockEnumerationOrder: (paths: string[]) => string[] = (paths) => paths;
|
|
15
|
+
jest.mock('globby', () => {
|
|
16
|
+
const actual = jest.requireActual('globby');
|
|
17
|
+
const reordered = async (...args: any[]) => mockEnumerationOrder([...(await actual(...args))].sort());
|
|
18
|
+
return Object.assign(reordered, actual);
|
|
19
|
+
});
|
|
20
|
+
|
|
21
|
+
const BUILD_TIMEOUT_MS = 120 * 1000;
|
|
22
|
+
|
|
23
|
+
describe('reproducible generated index', () => {
|
|
24
|
+
const fixture = new ExamplePackageFixture('reproducible', '@proteinjs/reflection-build-test-reproducible');
|
|
25
|
+
|
|
26
|
+
afterEach(() => {
|
|
27
|
+
mockEnumerationOrder = (paths) => paths;
|
|
28
|
+
});
|
|
29
|
+
|
|
30
|
+
afterAll(() => fixture.dispose());
|
|
31
|
+
|
|
32
|
+
test(
|
|
33
|
+
'two checkouts at different absolute paths ship byte-identical dist/generated/index.js and tarballs',
|
|
34
|
+
async () => {
|
|
35
|
+
const shipped = [];
|
|
36
|
+
for (const location of ['one/pkg', 'two/a/much/deeper/checkout/elsewhere']) {
|
|
37
|
+
const packageDir = fixture.materialize(location);
|
|
38
|
+
await fixture.generate(packageDir);
|
|
39
|
+
fixture.compile(packageDir);
|
|
40
|
+
const generatedJs = fs.readFileSync(path.join(packageDir, 'dist', 'generated', 'index.js'));
|
|
41
|
+
expect(generatedJs.toString()).not.toContain(packageDir);
|
|
42
|
+
shipped.push({ generatedJs: fixture.sha256(generatedJs), tarball: fixture.packSha256(packageDir) });
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
expect(shipped[1]).toEqual(shipped[0]);
|
|
46
|
+
},
|
|
47
|
+
BUILD_TIMEOUT_MS
|
|
48
|
+
);
|
|
49
|
+
|
|
50
|
+
test(
|
|
51
|
+
'the generated index does not depend on the order the source files are enumerated',
|
|
52
|
+
async () => {
|
|
53
|
+
const orders: { [name: string]: (paths: string[]) => string[] } = {
|
|
54
|
+
asEnumerated: (paths) => paths,
|
|
55
|
+
reversed: (paths) => [...paths].reverse(),
|
|
56
|
+
rotated: (paths) => [...paths.slice(3), ...paths.slice(0, 3)],
|
|
57
|
+
};
|
|
58
|
+
const generated: { [name: string]: string } = {};
|
|
59
|
+
for (const name of Object.keys(orders)) {
|
|
60
|
+
mockEnumerationOrder = orders[name];
|
|
61
|
+
generated[name] = await fixture.generate(fixture.materialize(`order-${name}/pkg`));
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
expect(generated.reversed).toBe(generated.asEnumerated);
|
|
65
|
+
expect(generated.rotated).toBe(generated.asEnumerated);
|
|
66
|
+
},
|
|
67
|
+
BUILD_TIMEOUT_MS
|
|
68
|
+
);
|
|
69
|
+
|
|
70
|
+
test(
|
|
71
|
+
'node filePaths are package-relative, with / separators and no leading ./',
|
|
72
|
+
async () => {
|
|
73
|
+
const graph = fixture.graphOf(await fixture.generate(fixture.materialize('relative/pkg')));
|
|
74
|
+
const gamma = graph.nodes.find((node) => node.v === fixture.qualified('GammaPlugin'));
|
|
75
|
+
expect(gamma?.value?.filePath).toBe('src/beta/deep/Gamma.ts');
|
|
76
|
+
|
|
77
|
+
const filePaths = graph.nodes.map((node) => node.value?.filePath).filter((filePath) => !!filePath);
|
|
78
|
+
expect(filePaths.length).toBeGreaterThan(0);
|
|
79
|
+
expect(filePaths.filter((filePath) => !/^src\/[A-Za-z]+(\/[A-Za-z]+)*\.ts$/.test(filePath))).toEqual([]);
|
|
80
|
+
},
|
|
81
|
+
BUILD_TIMEOUT_MS
|
|
82
|
+
);
|
|
83
|
+
|
|
84
|
+
test(
|
|
85
|
+
'nodes are emitted by file then qualified name, edges by child then parent, source links in node order',
|
|
86
|
+
async () => {
|
|
87
|
+
const generated = await fixture.generate(fixture.materialize('canonical/pkg'));
|
|
88
|
+
const graph = fixture.graphOf(generated);
|
|
89
|
+
const byCodeUnit = (keys: string[][]) =>
|
|
90
|
+
[...keys].sort((a, b) => {
|
|
91
|
+
const left = a.join('\0');
|
|
92
|
+
const right = b.join('\0');
|
|
93
|
+
return left < right ? -1 : left > right ? 1 : 0;
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
const nodeKeys = graph.nodes.map((node) => [node.value?.filePath || '', node.v]);
|
|
97
|
+
expect(nodeKeys).toEqual(byCodeUnit(nodeKeys));
|
|
98
|
+
expect(nodeKeys.slice(0, 2)).toEqual([
|
|
99
|
+
['', '@proteinjs/reflection/Loadable'],
|
|
100
|
+
['src/alpha/Alpha.ts', fixture.qualified('AardvarkPlugin')],
|
|
101
|
+
]);
|
|
102
|
+
|
|
103
|
+
const edgeKeys = graph.edges.map((edge) => [edge.v, edge.w]);
|
|
104
|
+
expect(edgeKeys).toEqual(byCodeUnit(edgeKeys));
|
|
105
|
+
|
|
106
|
+
const linkedNames = (generated.match(/^\t'[^']+'/gm) || []).map((line) => line.slice(2, -1));
|
|
107
|
+
const linkableNodeNames = graph.nodes
|
|
108
|
+
.filter((node) => node.value && (node.value.sourceType === 0 || node.value.sourceType === 2))
|
|
109
|
+
.map((node) => node.v);
|
|
110
|
+
expect(linkedNames).toEqual(linkableNodeNames);
|
|
111
|
+
},
|
|
112
|
+
BUILD_TIMEOUT_MS
|
|
113
|
+
);
|
|
114
|
+
});
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import * as fs from 'fs';
|
|
2
|
+
import * as path from 'path';
|
|
3
|
+
import { ExamplePackageFixture } from './ExamplePackageFixture';
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* One qualified name, one declaring file, for every name that reaches the generated index. The
|
|
7
|
+
* index holds one declaration per qualified name, so a name exported by two files would keep only
|
|
8
|
+
* the file parsed last and drop the other without a trace; the build refuses it instead. The rule
|
|
9
|
+
* applies after the prune: a name whose declarations are all pruned (types, consts, interfaces that
|
|
10
|
+
* are not Loadable) never reaches the index and is ordinary TypeScript.
|
|
11
|
+
*
|
|
12
|
+
* The enumeration is stubbed at the glob, as in the reproducible suite: the real glob's result in a
|
|
13
|
+
* fixed base order (code-unit sorted), reordered by `mockEnumerationOrder`.
|
|
14
|
+
*/
|
|
15
|
+
let mockEnumerationOrder: (paths: string[]) => string[] = (paths) => paths;
|
|
16
|
+
jest.mock('globby', () => {
|
|
17
|
+
const actual = jest.requireActual('globby');
|
|
18
|
+
const reordered = async (...args: any[]) => mockEnumerationOrder([...(await actual(...args))].sort());
|
|
19
|
+
return Object.assign(reordered, actual);
|
|
20
|
+
});
|
|
21
|
+
|
|
22
|
+
const BUILD_TIMEOUT_MS = 120 * 1000;
|
|
23
|
+
|
|
24
|
+
/** What one build did: the refusal's message, or the file whose declaration `name` holds in the index it wrote. */
|
|
25
|
+
const outcomeOf = (fixture: ExamplePackageFixture, packageDir: string, name: string) =>
|
|
26
|
+
fixture.generate(packageDir).then(
|
|
27
|
+
(generated) => ({
|
|
28
|
+
built:
|
|
29
|
+
fixture.graphOf(generated).nodes.find((node) => node.v === fixture.qualified(name))?.value?.filePath || null,
|
|
30
|
+
}),
|
|
31
|
+
(error: Error) => ({ refused: error.message })
|
|
32
|
+
);
|
|
33
|
+
|
|
34
|
+
describe('a name exported by two files', () => {
|
|
35
|
+
const sharedName = new ExamplePackageFixture('shared-name', '@proteinjs/reflection-build-test-shared-name');
|
|
36
|
+
const prunedPair = new ExamplePackageFixture(
|
|
37
|
+
'shared-name-pruned',
|
|
38
|
+
'@proteinjs/reflection-build-test-shared-name-pruned'
|
|
39
|
+
);
|
|
40
|
+
// Widget is declared by two files. Gear (src/gamma/Gear.ts) is declared twice by ONE file, an
|
|
41
|
+
// interface and a const, and reaches the index too: it is not a shared name, so it is not named.
|
|
42
|
+
const refusal = [
|
|
43
|
+
'reflection-build refused @proteinjs/reflection-build-test-shared-name: a name that reaches the generated index is declared in more than one file.',
|
|
44
|
+
'The index holds one declaration per qualified name, so the file parsed last would replace the others without a trace.',
|
|
45
|
+
'Give each declaration its own name (no index was written):',
|
|
46
|
+
' @proteinjs/reflection-build-test-shared-name/Widget: src/alpha/Widget.ts, src/beta/Widget.ts',
|
|
47
|
+
].join('\n');
|
|
48
|
+
|
|
49
|
+
afterEach(() => {
|
|
50
|
+
mockEnumerationOrder = (paths) => paths;
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
afterAll(() => {
|
|
54
|
+
sharedName.dispose();
|
|
55
|
+
prunedPair.dispose();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
test(
|
|
59
|
+
'reaching the index (an interface that extends Loadable, a class): the build refuses, naming the package, the name and both files, and writes no index',
|
|
60
|
+
async () => {
|
|
61
|
+
const packageDir = sharedName.materialize('refused/pkg');
|
|
62
|
+
expect(await outcomeOf(sharedName, packageDir, 'Widget')).toEqual({ refused: refusal });
|
|
63
|
+
expect(fs.existsSync(path.join(packageDir, 'generated', 'index.ts'))).toBe(false);
|
|
64
|
+
},
|
|
65
|
+
BUILD_TIMEOUT_MS
|
|
66
|
+
);
|
|
67
|
+
|
|
68
|
+
test(
|
|
69
|
+
'the refusal names the files in package-relative path order, whatever order they are enumerated in',
|
|
70
|
+
async () => {
|
|
71
|
+
mockEnumerationOrder = (paths) => [...paths].reverse();
|
|
72
|
+
expect(await outcomeOf(sharedName, sharedName.materialize('reversed/pkg'), 'Widget')).toEqual({
|
|
73
|
+
refused: refusal,
|
|
74
|
+
});
|
|
75
|
+
},
|
|
76
|
+
BUILD_TIMEOUT_MS
|
|
77
|
+
);
|
|
78
|
+
|
|
79
|
+
test(
|
|
80
|
+
'pruned before the index (a type and an interface; two consts): the build writes the index, without the names',
|
|
81
|
+
async () => {
|
|
82
|
+
const packageDir = prunedPair.materialize('built/pkg');
|
|
83
|
+
expect(await outcomeOf(prunedPair, packageDir, 'Shape')).toEqual({ built: null });
|
|
84
|
+
expect(await outcomeOf(prunedPair, packageDir, 'LIMITS')).toEqual({ built: null });
|
|
85
|
+
expect(await outcomeOf(prunedPair, packageDir, 'SquareTile')).toEqual({ built: 'src/alpha/Tile.ts' });
|
|
86
|
+
},
|
|
87
|
+
BUILD_TIMEOUT_MS
|
|
88
|
+
);
|
|
89
|
+
});
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { Loadable } from '@proteinjs/reflection';
|
|
2
|
+
|
|
3
|
+
export interface Plugin extends Loadable {
|
|
4
|
+
id: string;
|
|
5
|
+
}
|
|
6
|
+
|
|
7
|
+
export class AlphaPlugin implements Plugin {
|
|
8
|
+
id = 'alpha';
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export class AardvarkPlugin implements Plugin {
|
|
12
|
+
id = 'aardvark';
|
|
13
|
+
}
|