@travetto/manifest 7.0.0-rc.0 → 7.0.0-rc.1
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/package.json +1 -1
- package/src/context.ts +5 -5
- package/src/delta.ts +21 -21
- package/src/dependencies.ts +39 -38
- package/src/manifest-index.ts +26 -26
- package/src/module.ts +12 -12
- package/src/package.ts +6 -6
- package/src/path.ts +2 -2
- package/src/types/manifest.ts +1 -1
- package/src/types/package.ts +1 -1
- package/src/util.ts +5 -5
package/package.json
CHANGED
package/src/context.ts
CHANGED
|
@@ -8,25 +8,25 @@ import type { ManifestContext } from './types/context.ts';
|
|
|
8
8
|
type Pkg = Package & { path: string };
|
|
9
9
|
|
|
10
10
|
// eslint-disable-next-line no-bitwise
|
|
11
|
-
const toPort = (
|
|
12
|
-
const toPosix = (
|
|
11
|
+
const toPort = (location: string): number => (Math.abs([...location].reduce((a, b) => (a * 33) ^ b.charCodeAt(0), 5381)) % 29000) + 20000;
|
|
12
|
+
const toPosix = (location: string): string => location.replaceAll('\\', '/');
|
|
13
13
|
const readPackage = (file: string): Pkg => ({ ...JSON.parse(readFileSync(file, 'utf8')), path: toPosix(path.dirname(file)) });
|
|
14
14
|
|
|
15
15
|
/** Find package */
|
|
16
16
|
function findPackage(base: string, pred: (_p?: Pkg) => boolean): Pkg {
|
|
17
17
|
let folder = `${base}/.`;
|
|
18
|
-
let
|
|
18
|
+
let previous: string;
|
|
19
19
|
let pkg: Pkg | undefined;
|
|
20
20
|
const packages: Pkg[] = [];
|
|
21
21
|
|
|
22
22
|
do {
|
|
23
23
|
pkg && packages.push(pkg);
|
|
24
|
-
|
|
24
|
+
previous = folder;
|
|
25
25
|
folder = path.dirname(folder);
|
|
26
26
|
const folderPkg = path.resolve(folder, 'package.json');
|
|
27
27
|
pkg = existsSync(folderPkg) ? readPackage(folderPkg) : pkg;
|
|
28
28
|
} while (
|
|
29
|
-
|
|
29
|
+
previous !== folder && // Not at root
|
|
30
30
|
!pred(pkg) && // Matches criteria
|
|
31
31
|
!existsSync(path.resolve(folder, '.git')) // Not at source root
|
|
32
32
|
);
|
package/src/delta.ts
CHANGED
|
@@ -15,7 +15,7 @@ const VALID_SOURCE_FOLDERS = new Set<ManifestModuleFolderType>(['bin', 'src', 't
|
|
|
15
15
|
const VALID_SOURCE_TYPE = new Set<ManifestModuleFileType>(['js', 'ts', 'package-json']);
|
|
16
16
|
const VALID_OUTPUT_TYPE = new Set<ManifestModuleFileType>([...VALID_SOURCE_TYPE, 'typings']);
|
|
17
17
|
|
|
18
|
-
const TypedObject: { keys<T = unknown, K extends keyof T = keyof T>(
|
|
18
|
+
const TypedObject: { keys<T = unknown, K extends keyof T = keyof T>(item: T): K[] } & ObjectConstructor = Object;
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* Produce delta for the manifest
|
|
@@ -38,47 +38,47 @@ export class ManifestDeltaUtil {
|
|
|
38
38
|
const root = path.resolve(ctx.workspace.path, ctx.build.outputFolder, left.outputFolder);
|
|
39
39
|
const right = new Set(
|
|
40
40
|
(await ManifestModuleUtil.scanFolder(ctx, root, left.main))
|
|
41
|
-
.filter(
|
|
42
|
-
const type = ManifestModuleUtil.getFileType(
|
|
41
|
+
.filter(file => {
|
|
42
|
+
const type = ManifestModuleUtil.getFileType(file);
|
|
43
43
|
return VALID_SOURCE_TYPE.has(type);
|
|
44
44
|
})
|
|
45
|
-
.map(
|
|
45
|
+
.map(file => ManifestModuleUtil.withoutSourceExtension(file.replace(`${root}/`, '')))
|
|
46
46
|
);
|
|
47
47
|
|
|
48
|
-
for (const
|
|
49
|
-
const output = ManifestModuleUtil.withOutputExtension(`${outputFolder}/${left.outputFolder}/${
|
|
50
|
-
const [, ,
|
|
48
|
+
for (const file of Object.keys(left.files)) {
|
|
49
|
+
const output = ManifestModuleUtil.withOutputExtension(`${outputFolder}/${left.outputFolder}/${file}`);
|
|
50
|
+
const [, , leftTimestamp] = left.files[file];
|
|
51
51
|
const stat = await fs.stat(output).catch(() => undefined);
|
|
52
|
-
right.delete(ManifestModuleUtil.withoutSourceExtension(
|
|
52
|
+
right.delete(ManifestModuleUtil.withoutSourceExtension(file));
|
|
53
53
|
|
|
54
54
|
if (!stat) {
|
|
55
|
-
add(
|
|
55
|
+
add(file, 'added');
|
|
56
56
|
} else {
|
|
57
|
-
const
|
|
58
|
-
if (
|
|
59
|
-
add(
|
|
57
|
+
const rightTimestamp = this.#getNewest(stat);
|
|
58
|
+
if (leftTimestamp > rightTimestamp) {
|
|
59
|
+
add(file, 'changed');
|
|
60
60
|
}
|
|
61
61
|
}
|
|
62
62
|
}
|
|
63
63
|
// Deleted
|
|
64
|
-
for (const
|
|
65
|
-
add(
|
|
64
|
+
for (const file of right) {
|
|
65
|
+
add(file, 'removed');
|
|
66
66
|
}
|
|
67
67
|
return out;
|
|
68
68
|
}
|
|
69
69
|
|
|
70
70
|
/**
|
|
71
71
|
* Collapse all files in a module
|
|
72
|
-
* @param {ManifestModule}
|
|
72
|
+
* @param {ManifestModule} mod
|
|
73
73
|
* @returns {}
|
|
74
74
|
*/
|
|
75
|
-
static #flattenModuleFiles(
|
|
75
|
+
static #flattenModuleFiles(mod: ManifestModule): Record<string, ManifestModuleFile> {
|
|
76
76
|
const out: Record<string, ManifestModuleFile> = {};
|
|
77
|
-
for (const key of TypedObject.keys(
|
|
77
|
+
for (const key of TypedObject.keys(mod.files)) {
|
|
78
78
|
if (!VALID_SOURCE_FOLDERS.has(key)) {
|
|
79
79
|
continue;
|
|
80
80
|
}
|
|
81
|
-
for (const [name, type, date] of
|
|
81
|
+
for (const [name, type, date] of mod.files?.[key] ?? []) {
|
|
82
82
|
if (VALID_OUTPUT_TYPE.has(type)) {
|
|
83
83
|
out[name] = [name, type, date];
|
|
84
84
|
}
|
|
@@ -93,14 +93,14 @@ export class ManifestDeltaUtil {
|
|
|
93
93
|
static async produceDelta(manifest: ManifestRoot): Promise<DeltaEvent[]> {
|
|
94
94
|
const deltaLeft = Object.fromEntries(
|
|
95
95
|
Object.values(manifest.modules)
|
|
96
|
-
.map(
|
|
96
|
+
.map(mod => [mod.name, { ...mod, files: this.#flattenModuleFiles(mod) }])
|
|
97
97
|
);
|
|
98
98
|
|
|
99
99
|
const out: DeltaEvent[] = [];
|
|
100
100
|
const outputFolder = path.resolve(manifest.workspace.path, manifest.build.outputFolder);
|
|
101
101
|
|
|
102
|
-
for (const
|
|
103
|
-
out.push(...await this.#deltaModules(manifest, outputFolder,
|
|
102
|
+
for (const leftMod of Object.values(deltaLeft)) {
|
|
103
|
+
out.push(...await this.#deltaModules(manifest, outputFolder, leftMod));
|
|
104
104
|
}
|
|
105
105
|
|
|
106
106
|
return out;
|
package/src/dependencies.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { PackageUtil } from './package.ts';
|
|
2
2
|
import { path } from './path.ts';
|
|
3
3
|
|
|
4
|
-
import type { Package,
|
|
4
|
+
import type { Package, PackageDependencyType } from './types/package.ts';
|
|
5
5
|
import type { ManifestContext } from './types/context.ts';
|
|
6
6
|
import type { PackageModule } from './types/manifest.ts';
|
|
7
7
|
|
|
8
8
|
type CreateOpts = Partial<Pick<PackageModule, 'main' | 'workspace' | 'prod'>> & { roleRoot?: boolean, parent?: PackageModule };
|
|
9
9
|
|
|
10
|
-
type
|
|
10
|
+
type VisitableNode = {
|
|
11
11
|
/** Request package */
|
|
12
12
|
pkg: Package;
|
|
13
13
|
/** Children to visit */
|
|
@@ -24,7 +24,8 @@ type Req = {
|
|
|
24
24
|
export class PackageModuleVisitor {
|
|
25
25
|
|
|
26
26
|
static async visit(ctx: ManifestContext): Promise<Iterable<PackageModule>> {
|
|
27
|
-
const visitor = new PackageModuleVisitor(ctx, Object.fromEntries((await PackageUtil.resolveWorkspaces(ctx))
|
|
27
|
+
const visitor = new PackageModuleVisitor(ctx, Object.fromEntries((await PackageUtil.resolveWorkspaces(ctx))
|
|
28
|
+
.map(workspace => [workspace.name, workspace.path])));
|
|
28
29
|
return visitor.visit();
|
|
29
30
|
}
|
|
30
31
|
|
|
@@ -42,7 +43,7 @@ export class PackageModuleVisitor {
|
|
|
42
43
|
/**
|
|
43
44
|
* Build a package module
|
|
44
45
|
*/
|
|
45
|
-
#create(sourcePath: string, { main, workspace, prod = false, roleRoot = false, parent }: CreateOpts = {}):
|
|
46
|
+
#create(sourcePath: string, { main, workspace, prod = false, roleRoot = false, parent }: CreateOpts = {}): VisitableNode {
|
|
46
47
|
const pkg = PackageUtil.readPackage(sourcePath);
|
|
47
48
|
const value = this.#cache[sourcePath] ??= {
|
|
48
49
|
main,
|
|
@@ -55,31 +56,31 @@ export class PackageModuleVisitor {
|
|
|
55
56
|
outputFolder: `node_modules/${pkg.name}`,
|
|
56
57
|
state: {
|
|
57
58
|
childSet: new Set(), parentSet: new Set(), roleSet: new Set(), roleRoot,
|
|
58
|
-
travetto: pkg.travetto,
|
|
59
|
+
travetto: pkg.travetto, prodDependencies: new Set(Object.keys(pkg.dependencies ?? {}))
|
|
59
60
|
}
|
|
60
61
|
};
|
|
61
62
|
|
|
62
|
-
const
|
|
63
|
-
const children = Object.fromEntries(
|
|
63
|
+
const dependencies: PackageDependencyType[] = ['dependencies', ...(value.main ? ['devDependencies'] as const : [])];
|
|
64
|
+
const children = Object.fromEntries(dependencies.flatMap(dependency => Object.entries(pkg[dependency] ?? {})));
|
|
64
65
|
return { pkg, value, children, parent };
|
|
65
66
|
}
|
|
66
67
|
|
|
67
68
|
/**
|
|
68
69
|
* Get monorepo root includes
|
|
69
70
|
*/
|
|
70
|
-
#getMonoRootIncludes(parent:
|
|
71
|
+
#getMonoRootIncludes(parent: VisitableNode): VisitableNode[] {
|
|
71
72
|
if (!(this.#ctx.workspace.mono && !this.#ctx.main.folder)) { // If not mono root, bail
|
|
72
73
|
return [];
|
|
73
74
|
}
|
|
74
75
|
|
|
75
76
|
return Object.values(this.#workspaceModules)
|
|
76
|
-
.map(
|
|
77
|
+
.map(folder => this.#create(folder, { main: true, workspace: true, roleRoot: true, parent: parent.value }));
|
|
77
78
|
}
|
|
78
79
|
|
|
79
80
|
/**
|
|
80
81
|
* Determine default includes
|
|
81
82
|
*/
|
|
82
|
-
#getIncludes(parent:
|
|
83
|
+
#getIncludes(parent: VisitableNode): VisitableNode[] {
|
|
83
84
|
if (this.#ctx.workspace.mono && !this.#ctx.main.folder) { // If mono and not at mono root, bail
|
|
84
85
|
return [];
|
|
85
86
|
}
|
|
@@ -91,8 +92,8 @@ export class PackageModuleVisitor {
|
|
|
91
92
|
);
|
|
92
93
|
} else {
|
|
93
94
|
return Object.values(this.#workspaceModules)
|
|
94
|
-
.filter((
|
|
95
|
-
.map(
|
|
95
|
+
.filter((folder) => PackageUtil.readPackage(folder).travetto?.workspaceInclude)
|
|
96
|
+
.map(folder => this.#create(folder, { workspace: true, parent: parent.value }));
|
|
96
97
|
}
|
|
97
98
|
}
|
|
98
99
|
|
|
@@ -100,50 +101,50 @@ export class PackageModuleVisitor {
|
|
|
100
101
|
* Propagate prod, role information through graph
|
|
101
102
|
*/
|
|
102
103
|
async #complete(mods: Iterable<PackageModule>): Promise<PackageModule[]> {
|
|
103
|
-
const mapping = new Map([...mods].map(
|
|
104
|
+
const mapping = new Map([...mods].map(item => [item.name, { parent: new Set(item.state.parentSet), item }]));
|
|
104
105
|
|
|
105
106
|
// All first-level dependencies should have role filled in (for propagation)
|
|
106
|
-
for (const
|
|
107
|
-
|
|
108
|
-
for (const
|
|
109
|
-
const
|
|
110
|
-
if (
|
|
107
|
+
for (const dependency of [...mods].filter(mod => mod.state.roleRoot)) {
|
|
108
|
+
dependency.state.roleSet.clear(); // Ensure the roleRoot is empty
|
|
109
|
+
for (const child of dependency.state.childSet) { // Visit children
|
|
110
|
+
const childDependency = mapping.get(child)!.item;
|
|
111
|
+
if (childDependency.state.roleRoot) { continue; }
|
|
111
112
|
// Set roles for all top level modules
|
|
112
|
-
|
|
113
|
+
childDependency.state.roleSet = new Set(childDependency.state.travetto?.roles ?? ['std']);
|
|
113
114
|
}
|
|
114
115
|
}
|
|
115
116
|
|
|
116
117
|
// Visit all nodes
|
|
117
118
|
while (mapping.size > 0) {
|
|
118
|
-
const toProcess = [...mapping.values()].filter(
|
|
119
|
+
const toProcess = [...mapping.values()].filter(item => item.parent.size === 0);
|
|
119
120
|
if (!toProcess.length) {
|
|
120
121
|
throw new Error(`We have reached a cycle for ${[...mapping.keys()]}`);
|
|
121
122
|
}
|
|
122
123
|
// Propagate to children
|
|
123
|
-
for (const {
|
|
124
|
-
for (const
|
|
125
|
-
const child = mapping.get(
|
|
124
|
+
for (const { item } of toProcess) {
|
|
125
|
+
for (const childName of item.state.childSet) {
|
|
126
|
+
const child = mapping.get(childName);
|
|
126
127
|
if (!child) { continue; }
|
|
127
|
-
child.parent.delete(
|
|
128
|
+
child.parent.delete(item.name);
|
|
128
129
|
// Propagate roles from parent to child
|
|
129
|
-
if (!child.
|
|
130
|
-
for (const role of
|
|
131
|
-
child.
|
|
130
|
+
if (!child.item.state.roleRoot) {
|
|
131
|
+
for (const role of item.state.roleSet) {
|
|
132
|
+
child.item.state.roleSet.add(role);
|
|
132
133
|
}
|
|
133
134
|
}
|
|
134
135
|
// Allow prod to trickle down as needed
|
|
135
|
-
child.
|
|
136
|
+
child.item.prod ||= (item.prod && item.state.prodDependencies.has(childName));
|
|
136
137
|
}
|
|
137
138
|
}
|
|
138
139
|
// Remove from mapping
|
|
139
|
-
for (const {
|
|
140
|
-
mapping.delete(
|
|
140
|
+
for (const { item } of toProcess) {
|
|
141
|
+
mapping.delete(item.name);
|
|
141
142
|
}
|
|
142
143
|
}
|
|
143
144
|
|
|
144
145
|
// Mark as standard at the end
|
|
145
|
-
for (const
|
|
146
|
-
|
|
146
|
+
for (const dependency of [...mods].filter(mod => mod.state.roleRoot)) {
|
|
147
|
+
dependency.state.roleSet = new Set(['std']);
|
|
147
148
|
}
|
|
148
149
|
|
|
149
150
|
return [...mods].toSorted((a, b) => a.name.localeCompare(b.name));
|
|
@@ -154,12 +155,12 @@ export class PackageModuleVisitor {
|
|
|
154
155
|
*/
|
|
155
156
|
async visit(): Promise<Iterable<PackageModule>> {
|
|
156
157
|
const seen = new Set<PackageModule>();
|
|
157
|
-
const
|
|
158
|
+
const mainRequire = this.#create(this.#mainSourcePath, { main: true, workspace: true, roleRoot: true, prod: true });
|
|
158
159
|
|
|
159
160
|
const queue = [
|
|
160
|
-
|
|
161
|
-
...this.#getMonoRootIncludes(
|
|
162
|
-
...this.#getIncludes(
|
|
161
|
+
mainRequire,
|
|
162
|
+
...this.#getMonoRootIncludes(mainRequire),
|
|
163
|
+
...this.#getIncludes(mainRequire)
|
|
163
164
|
];
|
|
164
165
|
|
|
165
166
|
while (queue.length) {
|
|
@@ -181,8 +182,8 @@ export class PackageModuleVisitor {
|
|
|
181
182
|
}
|
|
182
183
|
|
|
183
184
|
const next = Object.entries(children)
|
|
184
|
-
.map(([
|
|
185
|
-
.map(
|
|
185
|
+
.map(([name, location]) => PackageUtil.resolveVersionPath(pkg, location) ?? PackageUtil.resolvePackagePath(name))
|
|
186
|
+
.map(location => this.#create(location, { parent: node }));
|
|
186
187
|
|
|
187
188
|
queue.push(...next);
|
|
188
189
|
}
|
package/src/manifest-index.ts
CHANGED
|
@@ -7,7 +7,7 @@ import { ManifestUtil } from './util.ts';
|
|
|
7
7
|
import type { ManifestModule, ManifestRoot, ManifestModuleFile, IndexedModule, IndexedFile, FindConfig } from './types/manifest.ts';
|
|
8
8
|
|
|
9
9
|
const TypedObject: {
|
|
10
|
-
keys<T = unknown, K extends keyof T = keyof T>(
|
|
10
|
+
keys<T = unknown, K extends keyof T = keyof T>(item: T): K[];
|
|
11
11
|
fromEntries<K extends string | symbol, V>(items: ([K, V] | readonly [K, V])[]): Record<K, V>;
|
|
12
12
|
entries<K extends Record<symbol | string, unknown>>(record: K): [keyof K, K[keyof K]][];
|
|
13
13
|
} & ObjectConstructor = Object;
|
|
@@ -56,19 +56,19 @@ export class ManifestIndex {
|
|
|
56
56
|
this.init(`${this.outputRoot}/node_modules/${module}`);
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
-
#moduleFiles(
|
|
60
|
-
return files.map(([
|
|
59
|
+
#moduleFiles(mod: ManifestModule, files: ManifestModuleFile[]): IndexedFile[] {
|
|
60
|
+
return files.map(([file, type, _ts, role = 'std']) => {
|
|
61
61
|
const isSource = type === 'ts' || type === 'js';
|
|
62
|
-
const sourceFile = path.resolve(this.#manifest.workspace.path,
|
|
63
|
-
const js = isSource ? ManifestModuleUtil.withOutputExtension(
|
|
64
|
-
const outputFile = this.#resolveOutput(
|
|
65
|
-
const modImport = `${
|
|
66
|
-
let id = `${
|
|
62
|
+
const sourceFile = path.resolve(this.#manifest.workspace.path, mod.sourceFolder, file);
|
|
63
|
+
const js = isSource ? ManifestModuleUtil.withOutputExtension(file) : file;
|
|
64
|
+
const outputFile = this.#resolveOutput(mod.outputFolder, js);
|
|
65
|
+
const modImport = `${mod.name}/${file}`;
|
|
66
|
+
let id = `${mod.name}:${file}`;
|
|
67
67
|
if (isSource) {
|
|
68
68
|
id = ManifestModuleUtil.withoutSourceExtension(id);
|
|
69
69
|
}
|
|
70
70
|
|
|
71
|
-
return { id, type, sourceFile, outputFile, import: modImport, role, relativeFile:
|
|
71
|
+
return { id, type, sourceFile, outputFile, import: modImport, role, relativeFile: file, module: mod.name };
|
|
72
72
|
});
|
|
73
73
|
}
|
|
74
74
|
|
|
@@ -82,13 +82,13 @@ export class ManifestIndex {
|
|
|
82
82
|
this.#arbitraryLookup = undefined;
|
|
83
83
|
|
|
84
84
|
this.#modules = Object.values(this.#manifest.modules)
|
|
85
|
-
.map(
|
|
86
|
-
...
|
|
87
|
-
outputPath: this.#resolveOutput(
|
|
88
|
-
sourcePath: path.resolve(this.#manifest.workspace.path,
|
|
85
|
+
.map(mod => ({
|
|
86
|
+
...mod,
|
|
87
|
+
outputPath: this.#resolveOutput(mod.outputFolder),
|
|
88
|
+
sourcePath: path.resolve(this.#manifest.workspace.path, mod.sourceFolder),
|
|
89
89
|
children: new Set(),
|
|
90
90
|
files: TypedObject.fromEntries(
|
|
91
|
-
TypedObject.entries(
|
|
91
|
+
TypedObject.entries(mod.files).map(([folder, files]) => [folder, this.#moduleFiles(mod, files ?? [])])
|
|
92
92
|
)
|
|
93
93
|
}));
|
|
94
94
|
|
|
@@ -103,12 +103,12 @@ export class ManifestIndex {
|
|
|
103
103
|
}
|
|
104
104
|
}
|
|
105
105
|
}
|
|
106
|
-
this.#modulesByName = Object.fromEntries(this.#modules.map(
|
|
106
|
+
this.#modulesByName = Object.fromEntries(this.#modules.map(mod => [mod.name, mod]));
|
|
107
107
|
|
|
108
108
|
// Store child information
|
|
109
109
|
for (const mod of this.#modules) {
|
|
110
|
-
for (const
|
|
111
|
-
this.#modulesByName[
|
|
110
|
+
for (const parent of mod.parents) {
|
|
111
|
+
this.#modulesByName[parent]?.children.add(mod.name);
|
|
112
112
|
}
|
|
113
113
|
}
|
|
114
114
|
}
|
|
@@ -125,7 +125,7 @@ export class ManifestIndex {
|
|
|
125
125
|
* @returns
|
|
126
126
|
*/
|
|
127
127
|
getWorkspaceModules(): IndexedModule[] {
|
|
128
|
-
return this.#modules.filter(
|
|
128
|
+
return this.#modules.filter(mod => mod.workspace);
|
|
129
129
|
}
|
|
130
130
|
|
|
131
131
|
/**
|
|
@@ -134,9 +134,9 @@ export class ManifestIndex {
|
|
|
134
134
|
*/
|
|
135
135
|
find(config: FindConfig): IndexedFile[] {
|
|
136
136
|
const searchSpace: IndexedFile[] = [];
|
|
137
|
-
for (const
|
|
138
|
-
if (config.module?.(
|
|
139
|
-
for (const [folder, files] of TypedObject.entries(
|
|
137
|
+
for (const mod of this.#modules) {
|
|
138
|
+
if (config.module?.(mod) ?? true) {
|
|
139
|
+
for (const [folder, files] of TypedObject.entries(mod.files)) {
|
|
140
140
|
if (config.folder?.(folder) ?? true) {
|
|
141
141
|
for (const file of files) {
|
|
142
142
|
if (
|
|
@@ -206,14 +206,14 @@ export class ManifestIndex {
|
|
|
206
206
|
*/
|
|
207
207
|
getModuleList(mode: 'workspace' | 'all', exprList: string = ''): Set<string> {
|
|
208
208
|
const allMods = Object.keys(this.#manifest.modules);
|
|
209
|
-
const active = new Set<string>(mode === 'workspace' ? this.getWorkspaceModules().map(
|
|
209
|
+
const active = new Set<string>(mode === 'workspace' ? this.getWorkspaceModules().map(item => item.name) : allMods);
|
|
210
210
|
|
|
211
211
|
for (const expr of exprList.split(/,/g)) {
|
|
212
|
-
const [,
|
|
212
|
+
const [, negative, mod] = expr.trim().match(/(-|[+])?([^+\- ]{1,150})$/) ?? [];
|
|
213
213
|
if (mod) {
|
|
214
214
|
const pattern = new RegExp(`^${mod.replace(/[*]/g, '.*')}$`);
|
|
215
|
-
for (const
|
|
216
|
-
active[
|
|
215
|
+
for (const moduleName of allMods.filter(item => pattern.test(item))) {
|
|
216
|
+
active[negative ? 'delete' : 'add'](moduleName);
|
|
217
217
|
}
|
|
218
218
|
}
|
|
219
219
|
}
|
|
@@ -248,7 +248,7 @@ export class ManifestIndex {
|
|
|
248
248
|
const base = this.#manifest.workspace.path;
|
|
249
249
|
const lookup = this.#arbitraryLookup ??= ManifestUtil.lookupTrie(
|
|
250
250
|
Object.values(this.#manifest.modules),
|
|
251
|
-
|
|
251
|
+
mod => mod.sourceFolder.split('/'),
|
|
252
252
|
sub =>
|
|
253
253
|
!existsSync(path.resolve(base, ...sub, 'package.json')) &&
|
|
254
254
|
!existsSync(path.resolve(base, ...sub, '.git'))
|
package/src/module.ts
CHANGED
|
@@ -18,8 +18,8 @@ const EXT_MAPPING: Record<string, ManifestModuleFileType> = {
|
|
|
18
18
|
};
|
|
19
19
|
|
|
20
20
|
const INDEX_FILES = new Set(
|
|
21
|
-
['__index__', '__index', 'index', 'jsx-runtime'].flatMap(
|
|
22
|
-
['ts', 'tsx', 'js'].map(ext => `${
|
|
21
|
+
['__index__', '__index', 'index', 'jsx-runtime'].flatMap(file =>
|
|
22
|
+
['ts', 'tsx', 'js'].map(ext => `${file}.${ext}`)
|
|
23
23
|
)
|
|
24
24
|
);
|
|
25
25
|
|
|
@@ -37,16 +37,16 @@ const SUPPORT_FILE_MAP: Record<string, ManifestModuleRole> = {
|
|
|
37
37
|
build: 'build'
|
|
38
38
|
};
|
|
39
39
|
|
|
40
|
-
const
|
|
40
|
+
const SUPPORT_FILE_REGEX = new RegExp(`^support[/](?<name>${Object.keys(SUPPORT_FILE_MAP).join('|')})[./]`);
|
|
41
41
|
|
|
42
42
|
export class ManifestModuleUtil {
|
|
43
43
|
|
|
44
44
|
static TYPINGS_EXT = '.d.ts';
|
|
45
45
|
static OUTPUT_EXT = '.js';
|
|
46
46
|
static SOURCE_DEF_EXT = '.ts';
|
|
47
|
-
static
|
|
48
|
-
static
|
|
49
|
-
static
|
|
47
|
+
static SOURCE_EXT_REGEX = /[.][cm]?[tj]sx?$/;
|
|
48
|
+
static TYPINGS_EXT_REGEX = /[.]d[.][cm]?ts$/;
|
|
49
|
+
static TYPINGS_WITH_MAP_EXT_REGEX = /[.]d[.][cm]?ts([.]map)?$/;
|
|
50
50
|
|
|
51
51
|
static #scanCache: Record<string, string[]> = {};
|
|
52
52
|
|
|
@@ -58,7 +58,7 @@ export class ManifestModuleUtil {
|
|
|
58
58
|
* Replace a source file's extension with a given value
|
|
59
59
|
*/
|
|
60
60
|
static #pathToExtension(sourceFile: string, ext: string): string {
|
|
61
|
-
return sourceFile.replace(ManifestModuleUtil.
|
|
61
|
+
return sourceFile.replace(ManifestModuleUtil.SOURCE_EXT_REGEX, ext);
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
/**
|
|
@@ -146,7 +146,7 @@ export class ManifestModuleUtil {
|
|
|
146
146
|
* Get file type for a file name
|
|
147
147
|
*/
|
|
148
148
|
static getFileRole(moduleFile: string): ManifestModuleRole | undefined {
|
|
149
|
-
const matched = SUPPORT_FILE_MAP[moduleFile.match(
|
|
149
|
+
const matched = SUPPORT_FILE_MAP[moduleFile.match(SUPPORT_FILE_REGEX)?.groups?.name ?? ''];
|
|
150
150
|
if (matched) {
|
|
151
151
|
return matched;
|
|
152
152
|
} else if (moduleFile.startsWith('test/')) {
|
|
@@ -199,9 +199,9 @@ export class ManifestModuleUtil {
|
|
|
199
199
|
*/
|
|
200
200
|
static async transformFile(moduleFile: string, full: string): Promise<ManifestModuleFile> {
|
|
201
201
|
const updated = this.#getNewest(await fs.stat(full).catch(() => ({ mtimeMs: 0, ctimeMs: 0 })));
|
|
202
|
-
const
|
|
202
|
+
const moduleFileTuple: ManifestModuleFile = [moduleFile, this.getFileType(moduleFile), updated];
|
|
203
203
|
const role = this.getFileRole(moduleFile);
|
|
204
|
-
return role ? [...
|
|
204
|
+
return role ? [...moduleFileTuple, role] : moduleFileTuple;
|
|
205
205
|
}
|
|
206
206
|
|
|
207
207
|
/**
|
|
@@ -234,8 +234,8 @@ export class ManifestModuleUtil {
|
|
|
234
234
|
*/
|
|
235
235
|
static async produceModules(ctx: ManifestContext): Promise<Record<string, ManifestModule>> {
|
|
236
236
|
const pkgs = await PackageModuleVisitor.visit(ctx);
|
|
237
|
-
const modules = await Promise.all([...pkgs].map(
|
|
238
|
-
return Object.fromEntries(modules.map(
|
|
237
|
+
const modules = await Promise.all([...pkgs].map(mod => this.describeModule(ctx, mod)));
|
|
238
|
+
return Object.fromEntries(modules.map(mod => [mod.name, mod]));
|
|
239
239
|
}
|
|
240
240
|
|
|
241
241
|
/**
|
package/src/package.ts
CHANGED
|
@@ -18,9 +18,9 @@ export class PackageUtil {
|
|
|
18
18
|
static #cache: Record<string, Package> = {};
|
|
19
19
|
static #workspaces: Record<string, PackageWorkspaceEntry[]> = {};
|
|
20
20
|
|
|
21
|
-
static #exec<T>(
|
|
21
|
+
static #exec<T>(workingDirectory: string, cmd: string): Promise<T> {
|
|
22
22
|
const env = { PATH: process.env.PATH, NODE_PATH: process.env.NODE_PATH };
|
|
23
|
-
const text = execSync(cmd, { cwd, encoding: 'utf8', env, stdio: ['pipe', 'pipe'] }).toString().trim();
|
|
23
|
+
const text = execSync(cmd, { cwd: workingDirectory, encoding: 'utf8', env, stdio: ['pipe', 'pipe'] }).toString().trim();
|
|
24
24
|
return JSON.parse(text);
|
|
25
25
|
}
|
|
26
26
|
|
|
@@ -55,13 +55,13 @@ export class PackageUtil {
|
|
|
55
55
|
return path.join(resolved.split(name)[0], name);
|
|
56
56
|
} catch { // When import lookup fails
|
|
57
57
|
let folder = root ?? process.cwd();
|
|
58
|
-
let
|
|
59
|
-
while (folder !==
|
|
58
|
+
let previous = '';
|
|
59
|
+
while (folder !== previous) {
|
|
60
60
|
const pkg = path.resolve(folder, 'node_modules', name, 'package.json');
|
|
61
61
|
if (existsSync(pkg)) {
|
|
62
62
|
return pkg;
|
|
63
63
|
}
|
|
64
|
-
|
|
64
|
+
previous = folder;
|
|
65
65
|
folder = path.dirname(folder);
|
|
66
66
|
}
|
|
67
67
|
}
|
|
@@ -106,7 +106,7 @@ export class PackageUtil {
|
|
|
106
106
|
case 'yarn':
|
|
107
107
|
case 'npm': {
|
|
108
108
|
const workspaces = await this.#exec<{ location: string, name: string }[]>(rootPath, 'npm query .workspace');
|
|
109
|
-
out = workspaces.map(
|
|
109
|
+
out = workspaces.map(mod => ({ path: path.resolve(ctx.workspace.path, mod.location), name: mod.name }));
|
|
110
110
|
break;
|
|
111
111
|
}
|
|
112
112
|
}
|
package/src/path.ts
CHANGED
|
@@ -29,7 +29,7 @@ export const path: posix.PlatformPath & {
|
|
|
29
29
|
isAbsolute: (file) => native.isAbsolute(toPosix(file)),
|
|
30
30
|
normalize: (file) => toPosix(native.normalize(toPosix(file))),
|
|
31
31
|
parse: (file) => native.parse(toPosix(file)),
|
|
32
|
-
format: (
|
|
32
|
+
format: (value) => toPosix(native.format(value)),
|
|
33
33
|
toNamespacedPath: (file) => toPosix(native.toNamespacedPath(toPosix(file))),
|
|
34
34
|
} : {
|
|
35
35
|
relative: (from, to) => posix.relative(toPosix(from), toPosix(to)),
|
|
@@ -38,7 +38,7 @@ export const path: posix.PlatformPath & {
|
|
|
38
38
|
isAbsolute: file => posix.isAbsolute(toPosix(file)),
|
|
39
39
|
normalize: file => posix.normalize(toPosix(file)),
|
|
40
40
|
parse: file => posix.parse(toPosix(file)),
|
|
41
|
-
format:
|
|
41
|
+
format: value => posix.format(value),
|
|
42
42
|
toNamespacedPath: file => toPosix(file),
|
|
43
43
|
}
|
|
44
44
|
};
|
package/src/types/manifest.ts
CHANGED
|
@@ -70,7 +70,7 @@ export type PackageModule = Omit<ManifestModule, 'files' | 'parents' | 'roles'>
|
|
|
70
70
|
/** Travetto package info */
|
|
71
71
|
travetto?: Package['travetto'];
|
|
72
72
|
/** Prod dependencies */
|
|
73
|
-
|
|
73
|
+
prodDependencies: Set<string>;
|
|
74
74
|
/** Set of parent package names */
|
|
75
75
|
parentSet: Set<string>;
|
|
76
76
|
/** Set of child package names */
|
package/src/types/package.ts
CHANGED
|
@@ -55,6 +55,6 @@ export type Package = {
|
|
|
55
55
|
publishConfig?: { access?: 'restricted' | 'public' };
|
|
56
56
|
};
|
|
57
57
|
|
|
58
|
-
export type
|
|
58
|
+
export type PackageDependencyType = 'dependencies' | 'devDependencies' | 'optionalDependencies' | 'peerDependencies';
|
|
59
59
|
|
|
60
60
|
export type PackageWorkspaceEntry = { name: string, path: string };
|
package/src/util.ts
CHANGED
|
@@ -36,8 +36,8 @@ export class ManifestUtil {
|
|
|
36
36
|
* Produce a production manifest from a given manifest
|
|
37
37
|
*/
|
|
38
38
|
static createProductionManifest(manifest: ManifestRoot): ManifestRoot {
|
|
39
|
-
const prodModules = Object.values(manifest.modules).filter(
|
|
40
|
-
const prodModNames = new Set([...prodModules.map(
|
|
39
|
+
const prodModules = Object.values(manifest.modules).filter(mod => mod.prod);
|
|
40
|
+
const prodModNames = new Set([...prodModules.map(mod => mod.name)]);
|
|
41
41
|
return {
|
|
42
42
|
generated: manifest.generated,
|
|
43
43
|
workspace: manifest.workspace,
|
|
@@ -48,8 +48,8 @@ export class ManifestUtil {
|
|
|
48
48
|
},
|
|
49
49
|
main: manifest.main,
|
|
50
50
|
modules: Object.fromEntries(
|
|
51
|
-
prodModules.map(
|
|
52
|
-
parents:
|
|
51
|
+
prodModules.map(mod => [mod.name, Object.assign(mod, {
|
|
52
|
+
parents: mod.parents.filter(parent => prodModNames.has(parent))
|
|
53
53
|
})])
|
|
54
54
|
),
|
|
55
55
|
};
|
|
@@ -136,7 +136,7 @@ export class ManifestUtil {
|
|
|
136
136
|
* Efficient lookup for path-based graphs
|
|
137
137
|
*/
|
|
138
138
|
static lookupTrie<T>(
|
|
139
|
-
inputs: T[], getPath: (
|
|
139
|
+
inputs: T[], getPath: (value: T) => string[], validateUnknown?: (pth: string[]) => boolean
|
|
140
140
|
): (pth: string[]) => T | undefined {
|
|
141
141
|
type TrieNode = { value?: T, subs: Record<string, TrieNode> };
|
|
142
142
|
const root: TrieNode = { subs: {} };
|