|
|
|
@@ -36,6 +36,7 @@ async function packageDigest(directory) {
|
|
|
|
|
/** Merge at package granularity: a scope such as @types is not a reserved package. */
|
|
|
|
|
export async function installDependencies({ destination, application, defaults = [], selected = {}, reserved = [] }) {
|
|
|
|
|
const entries = new Map();
|
|
|
|
|
const authored = new Set();
|
|
|
|
|
for (const root of defaults) for (const [name, file] of await packages(root)) entries.set(name, file);
|
|
|
|
|
for (const [name, file] of Object.entries(selected)) entries.set(name, file);
|
|
|
|
|
if (application)
|
|
|
|
@@ -44,10 +45,17 @@ export async function installDependencies({ destination, application, defaults =
|
|
|
|
|
const canonical = entries.get(name);
|
|
|
|
|
if (!canonical || (await packageDigest(file)) !== (await packageDigest(canonical)))
|
|
|
|
|
throw Error("Application dependencies cannot substitute selected React/client types or checked SDK: " + name);
|
|
|
|
|
} else entries.set(name, file);
|
|
|
|
|
} else {
|
|
|
|
|
entries.set(name, file);
|
|
|
|
|
authored.add(name);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
for (const [name, file] of entries) {
|
|
|
|
|
await fs.mkdir(path.dirname(path.join(destination, name)), { recursive: true });
|
|
|
|
|
await fs.symlink(file, path.join(destination, name));
|
|
|
|
|
// Application packages resolve siblings from this assembled node_modules.
|
|
|
|
|
// Keep SDK links canonical: copying those would duplicate branded types and
|
|
|
|
|
// React declarations from their own retained dependency closures.
|
|
|
|
|
if (authored.has(name)) await fs.cp(file, path.join(destination, name), { recursive: true, dereference: true });
|
|
|
|
|
else await fs.symlink(file, path.join(destination, name));
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|