plgg-bundle 0.0.2 → 0.0.5
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/bin/hook.d.mts +20 -0
- package/bin/hook.mjs +11 -2
- package/bin/plgg-bundle.mjs +9 -0
- package/bin/relocate.mjs +117 -0
- package/package.json +2 -2
- package/src/Dev/fixtures/aliasDir/index.ts +6 -0
- package/src/Dev/node/devServer.ts +5 -5
- package/src/Dev/node/httpAdapter.ts +1 -1
- package/src/Dev/node/scanGraph.ts +2 -2
- package/src/Dev/node/watch.ts +1 -1
- package/src/Dev/usecase/selfAliasResolve.spec.ts +68 -0
- package/src/domain/usecase/build.ts +2 -2
- package/src/domain/usecase/collectModules.spec.ts +283 -0
- package/src/domain/usecase/collectModules.ts +56 -3
- package/src/domain/usecase/deriveExternal.ts +2 -2
- package/src/domain/usecase/discoverWorkspace.spec.ts +111 -0
- package/src/domain/usecase/discoverWorkspace.ts +143 -28
- package/src/domain/usecase/emitDts.ts +4 -4
- package/src/domain/usecase/resolveSpecifier.ts +2 -2
- package/src/domain/usecase/resolveWorkspaceSpecifier.spec.ts +97 -0
- package/src/domain/usecase/resolveWorkspaceSpecifier.ts +40 -14
- package/src/domain/usecase/rewriteDtsAliases.ts +2 -2
- package/src/vendors/nodeFs.ts +22 -0
- package/src/vendors/nodeHttp.ts +14 -0
- package/src/vendors/nodePath.ts +15 -0
- package/src/vendors/nodeProc.ts +12 -0
- package/src/vendors/nodeUrl.ts +6 -0
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFileSync } from "
|
|
2
|
-
import { relative } from "
|
|
1
|
+
import { readFileSync } from "plgg-bundle/vendors/nodeFs";
|
|
2
|
+
import { relative } from "plgg-bundle/vendors/nodePath";
|
|
3
3
|
import { type External } from "plgg-bundle/domain/model/BundleConfig";
|
|
4
4
|
import { resolveSpecifier } from "plgg-bundle/domain/usecase/resolveSpecifier";
|
|
5
5
|
import { isExternal } from "plgg-bundle/domain/usecase/isExternal";
|
|
@@ -135,7 +135,7 @@ const linkModule = (
|
|
|
135
135
|
cjs: string,
|
|
136
136
|
acc: Map<string, Module>,
|
|
137
137
|
): ReadonlyArray<string> => {
|
|
138
|
-
const specifiers =
|
|
138
|
+
const specifiers = inlineRequireSpecifiers(file, cjs);
|
|
139
139
|
const externals: string[] = [];
|
|
140
140
|
const deps: string[] = [];
|
|
141
141
|
let rewritten = cjs;
|
|
@@ -165,6 +165,59 @@ const linkModule = (
|
|
|
165
165
|
return deps;
|
|
166
166
|
};
|
|
167
167
|
|
|
168
|
+
/**
|
|
169
|
+
* Literal requires that belong to the OUTER graph walk.
|
|
170
|
+
* Registry-installed plgg-family packages ship
|
|
171
|
+
* pre-bundled dist entries whose internal module table
|
|
172
|
+
* still contains strings like `require("src/index.ts")`.
|
|
173
|
+
* Those are parameters of the INNER registry runtime and
|
|
174
|
+
* must not be resolved or rewritten by this app bundle.
|
|
175
|
+
* Real top-level externals in that dist file remain, e.g.
|
|
176
|
+
* `require("plgg")`, and are still inlined by the app
|
|
177
|
+
* graph.
|
|
178
|
+
*/
|
|
179
|
+
const inlineRequireSpecifiers = (
|
|
180
|
+
file: string,
|
|
181
|
+
cjs: string,
|
|
182
|
+
): ReadonlyArray<string> => {
|
|
183
|
+
const specifiers = requireSpecifiers(cjs);
|
|
184
|
+
if (!isInstalledDist(file)) {
|
|
185
|
+
return specifiers;
|
|
186
|
+
}
|
|
187
|
+
const internal = bundledModuleIds(cjs);
|
|
188
|
+
return specifiers.filter((spec) => !internal.has(spec));
|
|
189
|
+
};
|
|
190
|
+
|
|
191
|
+
/**
|
|
192
|
+
* Whether a file is a registry-installed built JS entry.
|
|
193
|
+
* Source packages under the monorepo are never matched.
|
|
194
|
+
*/
|
|
195
|
+
const isInstalledDist = (file: string): boolean => {
|
|
196
|
+
const normalized = file.split("\\").join("/");
|
|
197
|
+
return (
|
|
198
|
+
normalized.includes("/node_modules/") &&
|
|
199
|
+
normalized.includes("/dist/") &&
|
|
200
|
+
normalized.endsWith(".js")
|
|
201
|
+
);
|
|
202
|
+
};
|
|
203
|
+
|
|
204
|
+
/**
|
|
205
|
+
* Module ids declared inside a plgg-bundle emitted
|
|
206
|
+
* registry object: `"src/x.ts": function (...) { ... }`.
|
|
207
|
+
*/
|
|
208
|
+
const bundledModuleIds = (
|
|
209
|
+
cjs: string,
|
|
210
|
+
): ReadonlySet<string> =>
|
|
211
|
+
new Set(
|
|
212
|
+
[
|
|
213
|
+
...cjs.matchAll(
|
|
214
|
+
/["']([^"']+)["']\s*:\s*function\s*\(/g,
|
|
215
|
+
),
|
|
216
|
+
].flatMap((m) =>
|
|
217
|
+
m[1] === undefined ? [] : [m[1]],
|
|
218
|
+
),
|
|
219
|
+
);
|
|
220
|
+
|
|
168
221
|
/**
|
|
169
222
|
* Read a source file, re-throwing with context.
|
|
170
223
|
*/
|
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import { readFileSync } from "
|
|
2
|
-
import { join } from "
|
|
1
|
+
import { readFileSync } from "plgg-bundle/vendors/nodeFs";
|
|
2
|
+
import { join } from "plgg-bundle/vendors/nodePath";
|
|
3
3
|
import { type External } from "plgg-bundle/domain/model/BundleConfig";
|
|
4
4
|
|
|
5
5
|
/**
|
|
@@ -4,6 +4,13 @@ import {
|
|
|
4
4
|
all,
|
|
5
5
|
toBe,
|
|
6
6
|
} from "plgg-test";
|
|
7
|
+
import {
|
|
8
|
+
mkdirSync,
|
|
9
|
+
mkdtempSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
writeFileSync,
|
|
12
|
+
} from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
7
14
|
import { join } from "node:path";
|
|
8
15
|
import { discoverWorkspace } from "plgg-bundle/domain/usecase/discoverWorkspace";
|
|
9
16
|
|
|
@@ -61,3 +68,107 @@ test("discoverWorkspace sorts longest-name first (so plgg-view beats plgg)", ()
|
|
|
61
68
|
);
|
|
62
69
|
return check(iView < iCore, toBe(true));
|
|
63
70
|
});
|
|
71
|
+
|
|
72
|
+
test("discoverWorkspace finds dist-only packages installed under node_modules", () => {
|
|
73
|
+
const root = fixtureRoot();
|
|
74
|
+
try {
|
|
75
|
+
const app = join(root, "packages", "app");
|
|
76
|
+
writePackage(app, "app", true);
|
|
77
|
+
writePackage(
|
|
78
|
+
join(app, "node_modules", "plgg"),
|
|
79
|
+
"plgg",
|
|
80
|
+
false,
|
|
81
|
+
);
|
|
82
|
+
const found = discoverWorkspace(app).find(
|
|
83
|
+
(p) => p.name === "plgg",
|
|
84
|
+
);
|
|
85
|
+
return all([
|
|
86
|
+
check(found?.kind, toBe("dist")),
|
|
87
|
+
check(
|
|
88
|
+
found?.exports.get("."),
|
|
89
|
+
toBe("./dist/index.es.js"),
|
|
90
|
+
),
|
|
91
|
+
]);
|
|
92
|
+
} finally {
|
|
93
|
+
rmSync(root, { recursive: true, force: true });
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
|
|
97
|
+
test("discoverWorkspace keeps a sibling source package before an installed duplicate", () => {
|
|
98
|
+
const root = fixtureRoot();
|
|
99
|
+
try {
|
|
100
|
+
const app = join(root, "packages", "app");
|
|
101
|
+
writePackage(app, "app", true);
|
|
102
|
+
writePackage(
|
|
103
|
+
join(root, "packages", "plgg"),
|
|
104
|
+
"plgg",
|
|
105
|
+
true,
|
|
106
|
+
);
|
|
107
|
+
writePackage(
|
|
108
|
+
join(app, "node_modules", "plgg"),
|
|
109
|
+
"plgg",
|
|
110
|
+
false,
|
|
111
|
+
);
|
|
112
|
+
const found = discoverWorkspace(app).find(
|
|
113
|
+
(p) => p.name === "plgg",
|
|
114
|
+
);
|
|
115
|
+
return check(found?.kind, toBe("source"));
|
|
116
|
+
} finally {
|
|
117
|
+
rmSync(root, { recursive: true, force: true });
|
|
118
|
+
}
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
test("discoverWorkspace finds installed deps of a sibling source package", () => {
|
|
122
|
+
const root = fixtureRoot();
|
|
123
|
+
try {
|
|
124
|
+
const app = join(root, "packages", "app");
|
|
125
|
+
writePackage(app, "app", true);
|
|
126
|
+
const local = join(root, "packages", "local");
|
|
127
|
+
writePackage(local, "local", true);
|
|
128
|
+
writePackage(
|
|
129
|
+
join(local, "node_modules", "plgg-ui"),
|
|
130
|
+
"plgg-ui",
|
|
131
|
+
false,
|
|
132
|
+
);
|
|
133
|
+
const found = discoverWorkspace(app).find(
|
|
134
|
+
(p) => p.name === "plgg-ui",
|
|
135
|
+
);
|
|
136
|
+
return check(found?.kind, toBe("dist"));
|
|
137
|
+
} finally {
|
|
138
|
+
rmSync(root, { recursive: true, force: true });
|
|
139
|
+
}
|
|
140
|
+
});
|
|
141
|
+
|
|
142
|
+
const fixtureRoot = (): string =>
|
|
143
|
+
mkdtempSync(
|
|
144
|
+
join(tmpdir(), "plgg-bundle-discover-"),
|
|
145
|
+
);
|
|
146
|
+
|
|
147
|
+
const writePackage = (
|
|
148
|
+
dir: string,
|
|
149
|
+
name: string,
|
|
150
|
+
source: boolean,
|
|
151
|
+
): void => {
|
|
152
|
+
mkdirSync(source ? join(dir, "src") : join(dir, "dist"), {
|
|
153
|
+
recursive: true,
|
|
154
|
+
});
|
|
155
|
+
writeFileSync(
|
|
156
|
+
join(dir, "package.json"),
|
|
157
|
+
JSON.stringify(
|
|
158
|
+
{
|
|
159
|
+
name,
|
|
160
|
+
type: "module",
|
|
161
|
+
exports: {
|
|
162
|
+
import: {
|
|
163
|
+
default: "./dist/index.es.js",
|
|
164
|
+
},
|
|
165
|
+
require: {
|
|
166
|
+
default: "./dist/index.cjs.js",
|
|
167
|
+
},
|
|
168
|
+
},
|
|
169
|
+
},
|
|
170
|
+
null,
|
|
171
|
+
2,
|
|
172
|
+
),
|
|
173
|
+
);
|
|
174
|
+
};
|
|
@@ -3,42 +3,69 @@ import {
|
|
|
3
3
|
readFileSync,
|
|
4
4
|
existsSync,
|
|
5
5
|
statSync,
|
|
6
|
-
} from "
|
|
7
|
-
import { join, dirname } from "
|
|
8
|
-
|
|
9
|
-
/**
|
|
10
|
-
* A
|
|
11
|
-
*
|
|
12
|
-
*
|
|
13
|
-
*
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
export
|
|
6
|
+
} from "plgg-bundle/vendors/nodeFs";
|
|
7
|
+
import { join, dirname } from "plgg-bundle/vendors/nodePath";
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A package the app bundler can INLINE. Monorepo
|
|
11
|
+
* siblings are inlined from `src`; registry-installed
|
|
12
|
+
* packages usually ship only `dist`, so the resolver
|
|
13
|
+
* inlines their built ESM entry instead.
|
|
14
|
+
*/
|
|
15
|
+
export type WorkspacePackage =
|
|
16
|
+
| SourceWorkspacePackage
|
|
17
|
+
| DistWorkspacePackage;
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* A source package, usually a sibling under `packages/`.
|
|
21
|
+
* Its public export subpaths are reversed from
|
|
22
|
+
* `exports` dist paths back to `src` entries.
|
|
23
|
+
*/
|
|
24
|
+
export type SourceWorkspacePackage = Readonly<{
|
|
25
|
+
kind: "source";
|
|
22
26
|
name: string;
|
|
23
27
|
dir: string;
|
|
28
|
+
srcDir: string;
|
|
24
29
|
/** export subpath (`"."` | `"./x"`) → dist default. */
|
|
25
30
|
exports: ReadonlyMap<string, string>;
|
|
26
31
|
}>;
|
|
27
32
|
|
|
28
33
|
/**
|
|
29
|
-
*
|
|
30
|
-
*
|
|
31
|
-
*
|
|
34
|
+
* A dist package, usually installed under the app's
|
|
35
|
+
* `node_modules`. It has no source in the consumer tree,
|
|
36
|
+
* so public export subpaths resolve to built ESM files.
|
|
37
|
+
*/
|
|
38
|
+
export type DistWorkspacePackage = Readonly<{
|
|
39
|
+
kind: "dist";
|
|
40
|
+
name: string;
|
|
41
|
+
dir: string;
|
|
42
|
+
distDir: string;
|
|
43
|
+
/** export subpath (`"."` | `"./x"`) → dist default. */
|
|
44
|
+
exports: ReadonlyMap<string, string>;
|
|
45
|
+
}>;
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Discover inlineable packages visible from `packageRoot`:
|
|
49
|
+
* sibling packages under the directory that holds
|
|
50
|
+
* `packageRoot`, plus packages installed in this package's
|
|
51
|
+
* own `node_modules` and in sibling source packages'
|
|
52
|
+
* `node_modules`. Siblings are listed first and win on
|
|
53
|
+
* duplicate names so monorepo builds keep reading source.
|
|
32
54
|
* Sorted longest-name-first so a later prefix match picks
|
|
33
|
-
* `plgg-view` over `plgg`.
|
|
55
|
+
* `plgg-view` over `plgg`.
|
|
34
56
|
*/
|
|
35
57
|
export const discoverWorkspace = (
|
|
36
58
|
packageRoot: string,
|
|
37
59
|
): ReadonlyArray<WorkspacePackage> => {
|
|
38
60
|
const siblingsDir = dirname(packageRoot);
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
61
|
+
const siblings = packageDirsIn(siblingsDir);
|
|
62
|
+
return uniqueByName([
|
|
63
|
+
...siblings,
|
|
64
|
+
...packageDirsIn(join(packageRoot, "node_modules")),
|
|
65
|
+
...siblings.flatMap((dir) =>
|
|
66
|
+
packageDirsIn(join(dir, "node_modules")),
|
|
67
|
+
),
|
|
68
|
+
])
|
|
42
69
|
.map(readPackage)
|
|
43
70
|
.flatMap((p) => (p === undefined ? [] : [p]))
|
|
44
71
|
.sort(
|
|
@@ -47,8 +74,61 @@ export const discoverWorkspace = (
|
|
|
47
74
|
};
|
|
48
75
|
|
|
49
76
|
/**
|
|
50
|
-
*
|
|
51
|
-
* `
|
|
77
|
+
* Direct package dirs under `dir`, including scoped
|
|
78
|
+
* `@scope/name` packages. Missing directories simply
|
|
79
|
+
* contribute no packages.
|
|
80
|
+
*/
|
|
81
|
+
const packageDirsIn = (
|
|
82
|
+
dir: string,
|
|
83
|
+
): ReadonlyArray<string> =>
|
|
84
|
+
existsSync(dir)
|
|
85
|
+
? readdirSync(dir).flatMap((name) =>
|
|
86
|
+
childPackageDirs(join(dir, name)),
|
|
87
|
+
)
|
|
88
|
+
: [];
|
|
89
|
+
|
|
90
|
+
/**
|
|
91
|
+
* The package dirs represented by one child of a package
|
|
92
|
+
* collection directory. A scope directory fans out one
|
|
93
|
+
* level; ordinary package directories are returned as-is.
|
|
94
|
+
*/
|
|
95
|
+
const childPackageDirs = (
|
|
96
|
+
dir: string,
|
|
97
|
+
): ReadonlyArray<string> => {
|
|
98
|
+
if (!statSync(dir).isDirectory()) {
|
|
99
|
+
return [];
|
|
100
|
+
}
|
|
101
|
+
if (isPackageDir(dir)) {
|
|
102
|
+
return [dir];
|
|
103
|
+
}
|
|
104
|
+
return readdirSync(dir)
|
|
105
|
+
.map((name) => join(dir, name))
|
|
106
|
+
.filter(isPackageDir);
|
|
107
|
+
};
|
|
108
|
+
|
|
109
|
+
/**
|
|
110
|
+
* Keep the first directory for each package name. The
|
|
111
|
+
* caller supplies siblings before node_modules, so a local
|
|
112
|
+
* source package beats an installed copy.
|
|
113
|
+
*/
|
|
114
|
+
const uniqueByName = (
|
|
115
|
+
dirs: ReadonlyArray<string>,
|
|
116
|
+
): ReadonlyArray<string> => {
|
|
117
|
+
const seen = new Set<string>();
|
|
118
|
+
const out: string[] = [];
|
|
119
|
+
for (const dir of dirs) {
|
|
120
|
+
const name = packageName(dir);
|
|
121
|
+
if (name === undefined || seen.has(name)) {
|
|
122
|
+
continue;
|
|
123
|
+
}
|
|
124
|
+
seen.add(name);
|
|
125
|
+
out.push(dir);
|
|
126
|
+
}
|
|
127
|
+
return out;
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Whether a path is a directory holding a package.json.
|
|
52
132
|
*/
|
|
53
133
|
const isPackageDir = (dir: string): boolean =>
|
|
54
134
|
statSync(dir).isDirectory() &&
|
|
@@ -66,9 +146,44 @@ const readPackage = (
|
|
|
66
146
|
join(dir, "package.json"),
|
|
67
147
|
);
|
|
68
148
|
const name = pkg["name"];
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
149
|
+
if (typeof name !== "string") {
|
|
150
|
+
return undefined;
|
|
151
|
+
}
|
|
152
|
+
const srcDir = join(dir, "src");
|
|
153
|
+
if (existsSync(srcDir)) {
|
|
154
|
+
return {
|
|
155
|
+
kind: "source",
|
|
156
|
+
name,
|
|
157
|
+
dir,
|
|
158
|
+
srcDir,
|
|
159
|
+
exports: exportMap(pkg),
|
|
160
|
+
};
|
|
161
|
+
}
|
|
162
|
+
const distDir = join(dir, "dist");
|
|
163
|
+
if (existsSync(distDir)) {
|
|
164
|
+
return {
|
|
165
|
+
kind: "dist",
|
|
166
|
+
name,
|
|
167
|
+
dir,
|
|
168
|
+
distDir,
|
|
169
|
+
exports: exportMap(pkg),
|
|
170
|
+
};
|
|
171
|
+
}
|
|
172
|
+
return undefined;
|
|
173
|
+
};
|
|
174
|
+
|
|
175
|
+
/**
|
|
176
|
+
* Read only the package name for de-duplication.
|
|
177
|
+
*/
|
|
178
|
+
const packageName = (
|
|
179
|
+
dir: string,
|
|
180
|
+
): string | undefined => {
|
|
181
|
+
if (!isPackageDir(dir)) {
|
|
182
|
+
return undefined;
|
|
183
|
+
}
|
|
184
|
+
const pkg = parseJson(join(dir, "package.json"));
|
|
185
|
+
const name = pkg["name"];
|
|
186
|
+
return typeof name === "string" ? name : undefined;
|
|
72
187
|
};
|
|
73
188
|
|
|
74
189
|
/**
|
|
@@ -1,16 +1,16 @@
|
|
|
1
|
-
import { spawnSync } from "
|
|
1
|
+
import { spawnSync } from "plgg-bundle/vendors/nodeProc";
|
|
2
2
|
import {
|
|
3
3
|
join,
|
|
4
4
|
dirname,
|
|
5
5
|
basename,
|
|
6
6
|
relative,
|
|
7
|
-
} from "
|
|
7
|
+
} from "plgg-bundle/vendors/nodePath";
|
|
8
8
|
import {
|
|
9
9
|
existsSync,
|
|
10
10
|
writeFileSync,
|
|
11
11
|
rmSync,
|
|
12
|
-
} from "
|
|
13
|
-
import { createRequire } from "
|
|
12
|
+
} from "plgg-bundle/vendors/nodeFs";
|
|
13
|
+
import { createRequire } from "plgg-bundle/vendors/nodeProc";
|
|
14
14
|
import { rewriteDtsAliases } from "plgg-bundle/domain/usecase/rewriteDtsAliases";
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -1,9 +1,9 @@
|
|
|
1
|
-
import { existsSync, statSync } from "
|
|
1
|
+
import { existsSync, statSync } from "plgg-bundle/vendors/nodeFs";
|
|
2
2
|
import {
|
|
3
3
|
dirname,
|
|
4
4
|
resolve,
|
|
5
5
|
join,
|
|
6
|
-
} from "
|
|
6
|
+
} from "plgg-bundle/vendors/nodePath";
|
|
7
7
|
|
|
8
8
|
/**
|
|
9
9
|
* Resolve a bare import specifier to an absolute source
|
|
@@ -4,6 +4,13 @@ import {
|
|
|
4
4
|
all,
|
|
5
5
|
toBe,
|
|
6
6
|
} from "plgg-test";
|
|
7
|
+
import {
|
|
8
|
+
mkdirSync,
|
|
9
|
+
mkdtempSync,
|
|
10
|
+
rmSync,
|
|
11
|
+
writeFileSync,
|
|
12
|
+
} from "node:fs";
|
|
13
|
+
import { tmpdir } from "node:os";
|
|
7
14
|
import { join } from "node:path";
|
|
8
15
|
import { discoverWorkspace } from "plgg-bundle/domain/usecase/discoverWorkspace";
|
|
9
16
|
import { resolveWorkspaceSpecifier } from "plgg-bundle/domain/usecase/resolveWorkspaceSpecifier";
|
|
@@ -77,3 +84,93 @@ test("returns undefined for a non-workspace specifier", () =>
|
|
|
77
84
|
check(resolve("react"), toBe(undefined)),
|
|
78
85
|
check(resolve("node:fs"), toBe(undefined)),
|
|
79
86
|
]));
|
|
87
|
+
|
|
88
|
+
test("resolves an installed dist-only package export to its built ESM file", () => {
|
|
89
|
+
const root = fixtureRoot();
|
|
90
|
+
try {
|
|
91
|
+
const app = join(root, "packages", "app");
|
|
92
|
+
writePackage(app, "app", true);
|
|
93
|
+
writePackage(
|
|
94
|
+
join(app, "node_modules", "plgg"),
|
|
95
|
+
"plgg",
|
|
96
|
+
false,
|
|
97
|
+
);
|
|
98
|
+
const installed = discoverWorkspace(app);
|
|
99
|
+
return check(
|
|
100
|
+
resolveWorkspaceSpecifier({
|
|
101
|
+
specifier: "plgg",
|
|
102
|
+
fromFile: join(app, "src", "main.ts"),
|
|
103
|
+
packages: installed,
|
|
104
|
+
}),
|
|
105
|
+
toBe(
|
|
106
|
+
join(
|
|
107
|
+
app,
|
|
108
|
+
"node_modules",
|
|
109
|
+
"plgg",
|
|
110
|
+
"dist",
|
|
111
|
+
"index.es.js",
|
|
112
|
+
),
|
|
113
|
+
),
|
|
114
|
+
);
|
|
115
|
+
} finally {
|
|
116
|
+
rmSync(root, { recursive: true, force: true });
|
|
117
|
+
}
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test("does not resolve internal paths from a dist-only installed package", () => {
|
|
121
|
+
const root = fixtureRoot();
|
|
122
|
+
try {
|
|
123
|
+
const app = join(root, "packages", "app");
|
|
124
|
+
writePackage(app, "app", true);
|
|
125
|
+
writePackage(
|
|
126
|
+
join(app, "node_modules", "plgg"),
|
|
127
|
+
"plgg",
|
|
128
|
+
false,
|
|
129
|
+
);
|
|
130
|
+
const installed = discoverWorkspace(app);
|
|
131
|
+
return check(
|
|
132
|
+
resolveWorkspaceSpecifier({
|
|
133
|
+
specifier: "plgg/Atomics/Num",
|
|
134
|
+
fromFile: join(app, "src", "main.ts"),
|
|
135
|
+
packages: installed,
|
|
136
|
+
}),
|
|
137
|
+
toBe(undefined),
|
|
138
|
+
);
|
|
139
|
+
} finally {
|
|
140
|
+
rmSync(root, { recursive: true, force: true });
|
|
141
|
+
}
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
const fixtureRoot = (): string =>
|
|
145
|
+
mkdtempSync(join(tmpdir(), "plgg-bundle-resolve-"));
|
|
146
|
+
|
|
147
|
+
const writePackage = (
|
|
148
|
+
dir: string,
|
|
149
|
+
name: string,
|
|
150
|
+
source: boolean,
|
|
151
|
+
): void => {
|
|
152
|
+
const codeDir = source
|
|
153
|
+
? join(dir, "src")
|
|
154
|
+
: join(dir, "dist");
|
|
155
|
+
mkdirSync(codeDir, { recursive: true });
|
|
156
|
+
writeFileSync(join(codeDir, "index.es.js"), "");
|
|
157
|
+
writeFileSync(
|
|
158
|
+
join(dir, "package.json"),
|
|
159
|
+
JSON.stringify(
|
|
160
|
+
{
|
|
161
|
+
name,
|
|
162
|
+
type: "module",
|
|
163
|
+
exports: {
|
|
164
|
+
import: {
|
|
165
|
+
default: "./dist/index.es.js",
|
|
166
|
+
},
|
|
167
|
+
require: {
|
|
168
|
+
default: "./dist/index.cjs.js",
|
|
169
|
+
},
|
|
170
|
+
},
|
|
171
|
+
},
|
|
172
|
+
null,
|
|
173
|
+
2,
|
|
174
|
+
),
|
|
175
|
+
);
|
|
176
|
+
};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { basename, join } from "
|
|
1
|
+
import { basename, join } from "plgg-bundle/vendors/nodePath";
|
|
2
2
|
import {
|
|
3
3
|
resolveSpecifier,
|
|
4
4
|
isRelative,
|
|
@@ -8,11 +8,12 @@ import { type WorkspacePackage } from "plgg-bundle/domain/usecase/discoverWorksp
|
|
|
8
8
|
|
|
9
9
|
/**
|
|
10
10
|
* App-mode resolver: resolve an import specifier to an
|
|
11
|
-
* absolute
|
|
12
|
-
* (`plgg`, `
|
|
13
|
-
*
|
|
14
|
-
*
|
|
15
|
-
*
|
|
11
|
+
* absolute inlineable file. Source packages
|
|
12
|
+
* (`packages/plgg`, local `file:` siblings) resolve to
|
|
13
|
+
* `src`; published packages installed in `node_modules`
|
|
14
|
+
* resolve to their built ESM `dist` export. This is the
|
|
15
|
+
* mirror of the library externalization — the leaf app is
|
|
16
|
+
* where bundling deps is correct.
|
|
16
17
|
*
|
|
17
18
|
* Order:
|
|
18
19
|
* 1. relative (`./x`) → resolve against the importer,
|
|
@@ -75,8 +76,8 @@ const matchPackage = (
|
|
|
75
76
|
|
|
76
77
|
/**
|
|
77
78
|
* Resolve a specifier already known to belong to `pkg`:
|
|
78
|
-
* a declared export subpath
|
|
79
|
-
* else
|
|
79
|
+
* a declared export subpath through the package kind,
|
|
80
|
+
* else a source package's internal self-alias path.
|
|
80
81
|
*/
|
|
81
82
|
const resolveInPackage = (
|
|
82
83
|
pkg: WorkspacePackage,
|
|
@@ -89,17 +90,42 @@ const resolveInPackage = (
|
|
|
89
90
|
pkg.name.length + 1,
|
|
90
91
|
)}`;
|
|
91
92
|
const dist = pkg.exports.get(subpath);
|
|
92
|
-
return
|
|
93
|
+
return pkg.kind === "source"
|
|
94
|
+
? resolveInSourcePackage(pkg, specifier, dist)
|
|
95
|
+
: resolveInDistPackage(pkg, dist);
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Source packages keep the previous monorepo behavior:
|
|
100
|
+
* public exports reverse their emitted dist stem back to
|
|
101
|
+
* source; non-export self-alias paths resolve under `src`.
|
|
102
|
+
*/
|
|
103
|
+
const resolveInSourcePackage = (
|
|
104
|
+
pkg: WorkspacePackage & { kind: "source" },
|
|
105
|
+
specifier: string,
|
|
106
|
+
dist: string | undefined,
|
|
107
|
+
): string | undefined =>
|
|
108
|
+
dist === undefined
|
|
93
109
|
? resolveSpecifier({
|
|
94
110
|
specifier,
|
|
95
111
|
fromFile: pkg.dir,
|
|
96
112
|
aliasPrefix: pkg.name,
|
|
97
|
-
aliasSrcRoot:
|
|
113
|
+
aliasSrcRoot: pkg.srcDir,
|
|
98
114
|
})
|
|
99
|
-
: pickExisting(
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
115
|
+
: pickExisting(join(pkg.srcDir, srcStem(dist)));
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* Dist-only packages can safely resolve only declared
|
|
119
|
+
* public exports. Internal self-alias paths are not
|
|
120
|
+
* available in a consumer install.
|
|
121
|
+
*/
|
|
122
|
+
const resolveInDistPackage = (
|
|
123
|
+
pkg: WorkspacePackage & { kind: "dist" },
|
|
124
|
+
dist: string | undefined,
|
|
125
|
+
): string | undefined =>
|
|
126
|
+
dist === undefined
|
|
127
|
+
? undefined
|
|
128
|
+
: pickExisting(join(pkg.dir, dist));
|
|
103
129
|
|
|
104
130
|
/**
|
|
105
131
|
* The source stem implied by an export's dist default —
|
|
@@ -3,12 +3,12 @@ import {
|
|
|
3
3
|
statSync,
|
|
4
4
|
readFileSync,
|
|
5
5
|
writeFileSync,
|
|
6
|
-
} from "
|
|
6
|
+
} from "plgg-bundle/vendors/nodeFs";
|
|
7
7
|
import {
|
|
8
8
|
join,
|
|
9
9
|
dirname,
|
|
10
10
|
relative,
|
|
11
|
-
} from "
|
|
11
|
+
} from "plgg-bundle/vendors/nodePath";
|
|
12
12
|
|
|
13
13
|
/**
|
|
14
14
|
* Rewrite the package self-alias (`<prefix>/<sub>`) in
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The filesystem anti-corruption layer for plgg-bundle — the
|
|
3
|
+
* ONLY place the bundler's build usecases reach `node:fs`, kept
|
|
4
|
+
* under `vendors/` so the domain speaks these named operations,
|
|
5
|
+
* not the driver. Re-exported (not re-wrapped) to preserve the
|
|
6
|
+
* exact node types; plgg-bundle's throw-at-the-boundary error
|
|
7
|
+
* model (see BundleConfig) means a failed fs call surfaces as a
|
|
8
|
+
* thrown Error the entrypoint catches — the seam is the import
|
|
9
|
+
* chokepoint, so the vendor-boundary gate holds unexempted.
|
|
10
|
+
*/
|
|
11
|
+
export {
|
|
12
|
+
writeFileSync,
|
|
13
|
+
mkdirSync,
|
|
14
|
+
rmSync,
|
|
15
|
+
renameSync,
|
|
16
|
+
existsSync,
|
|
17
|
+
readdirSync,
|
|
18
|
+
readFileSync,
|
|
19
|
+
statSync,
|
|
20
|
+
watch,
|
|
21
|
+
type FSWatcher,
|
|
22
|
+
} from "node:fs";
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The HTTP anti-corruption layer for plgg-bundle's DEV server —
|
|
3
|
+
* the ONLY place the node:http ⇄ Web-standard bridge reaches
|
|
4
|
+
* `node:http`. Confined here so the dev orchestration stays
|
|
5
|
+
* `node:`-free (the vendor-boundary gate); a plgg-free
|
|
6
|
+
* reimplementation of `serve` (the toolchain must not import a
|
|
7
|
+
* library it builds).
|
|
8
|
+
*/
|
|
9
|
+
export {
|
|
10
|
+
createServer,
|
|
11
|
+
type IncomingMessage,
|
|
12
|
+
type ServerResponse,
|
|
13
|
+
type Server,
|
|
14
|
+
} from "node:http";
|