Skip to content

Commit e716814

Browse files
committed
POC - fresh with npm imports
1 parent 6d50f45 commit e716814

File tree

1 file changed

+129
-1
lines changed

1 file changed

+129
-1
lines changed

src/dev/mod.ts

Lines changed: 129 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -160,7 +160,135 @@ export async function dev(base: string, entrypoint: string) {
160160

161161
if (manifestChanged) await generate(dir, newManifest);
162162

163-
await import(entrypoint);
163+
if (await updateNpmSpecifiers(newManifest, dir)) {
164+
// reset
165+
await dev(base, entrypoint);
166+
} else {
167+
await import(entrypoint);
168+
}
169+
}
170+
171+
async function updateNpmSpecifiers(_manifest: Manifest, dir: string) {
172+
const importMapPath = join(dir, "import_map.json");
173+
const importMapText = await Deno.readTextFile(importMapPath);
174+
const importMap = JSON.parse(importMapText);
175+
const originalImportMapSnapshot = JSON.stringify(importMap);
176+
// todo: don't mutate the provided object in getDenoInfo
177+
const denoInfo = await getDenoInfo(dir, importMap);
178+
179+
if (!denoInfo.npmPackages) {
180+
return false;
181+
}
182+
183+
const foundReferences = new Set<string>();
184+
const npmPackageReferences = [];
185+
186+
// todo: only analyze the tree of islands
187+
for (const module of denoInfo.modules) {
188+
if (module.dependencies) {
189+
for (const dep of module.dependencies) {
190+
if (dep.npmPackage != null && !foundReferences.has(dep.specifier)) {
191+
npmPackageReferences.push({
192+
specifier: dep.specifier,
193+
package: dep.npmPackage,
194+
});
195+
foundReferences.add(dep.specifier);
196+
}
197+
}
198+
}
199+
}
200+
201+
for (const reference of npmPackageReferences) {
202+
const pkg = denoInfo.npmPackages![reference.package];
203+
let esmUrl = `https://esm.sh/${pkg.name}@${pkg.version}`;
204+
const deps = pkg.dependencies.map((depId) => {
205+
const dep = denoInfo.npmPackages![depId];
206+
return `${dep.name}@${dep.version}`;
207+
});
208+
if (deps.length > 0) {
209+
// very buggy
210+
// esmUrl += "?deps=" + deps.join(",");
211+
}
212+
importMap.imports[reference.specifier] = esmUrl;
213+
}
214+
if (originalImportMapSnapshot === JSON.stringify(importMap)) {
215+
return false;
216+
}
217+
218+
// todo: use deno fmt
219+
await Deno.writeTextFile(
220+
importMapPath,
221+
JSON.stringify(importMap, undefined, 2) + "\n",
222+
);
223+
return true;
224+
}
225+
226+
interface DenoInfo {
227+
modules: DenoInfoModule[];
228+
npmPackages?: Record<string, DenoInfoNpmPackage>;
229+
}
230+
231+
interface DenoInfoModule {
232+
specifier: string;
233+
dependencies?: DenoInfoModuleDependency[];
234+
}
235+
236+
interface DenoInfoModuleDependency {
237+
specifier: string;
238+
code: DenoInfoModuleDependencyCode;
239+
npmPackage?: string;
240+
}
241+
242+
interface DenoInfoModuleDependencyCode {
243+
specifier: string;
244+
}
245+
246+
interface DenoInfoNpmPackage {
247+
name: string;
248+
version: string;
249+
dependencies: string[];
250+
}
251+
252+
async function getDenoInfo(dir: string, importMap: any) {
253+
for (const key of Object.keys(importMap.imports)) {
254+
if (isNpmPackageReference(key)) {
255+
delete importMap.imports[key];
256+
}
257+
}
258+
259+
const importMapUri = `data:application/json;base64,${
260+
btoa(JSON.stringify(importMap))
261+
}`;
262+
const proc = Deno.run({
263+
cmd: [
264+
"V:\\deno\\target\\debug\\deno.exe",
265+
"info",
266+
"--import-map",
267+
importMapUri,
268+
"--json",
269+
"main.ts",
270+
],
271+
cwd: dir,
272+
stdout: "piped",
273+
stderr: "piped",
274+
});
275+
const [out, stderrOutput] = await Promise.all([
276+
proc.output(),
277+
proc.stderrOutput(),
278+
]);
279+
const status = await proc.status();
280+
if (!status.success) {
281+
console.error(new TextDecoder().decode(stderrOutput));
282+
throw new Error("Failed getting deno info.");
283+
}
284+
proc.close();
285+
const outputText = new TextDecoder().decode(out);
286+
const outputData = JSON.parse(outputText);
287+
return outputData as DenoInfo;
288+
}
289+
290+
function isNpmPackageReference(rawText: string) {
291+
return rawText.startsWith("npm:");
164292
}
165293

166294
function arraysEqual<T>(a: T[], b: T[]): boolean {

0 commit comments

Comments
 (0)