import-plan.js (1645B)
1 import { sourceText } from './compiler.js'; 2 // Header meaning comes from the native compiler. Insert at the beginning, which 3 // preserves every original character and keeps the directive in the header. 4 export function importPlan(source, stage, manifest, alias) { 5 if (!/^[a-f0-9]{64}$/u.test(manifest)) 6 throw new Error('Invalid module manifest'); 7 if (!/^[A-Za-z_][A-Za-z0-9_]*$/u.test(alias) || alias.length > 128) 8 throw new Error('Use a namespace starting with a letter or underscore, followed by letters, digits or underscores.'); 9 const statement = `!import ${manifest} as ${alias}\n`; 10 const existing = stage.imports.find(([ref]) => ref === manifest); 11 if (existing) { 12 // Locate a display selection in the compiler-masked header; this has no role 13 // in parsing or compiling source. Fall back to selecting the whole source. 14 let offset = 0, range = { start: 0, end: source.length }; 15 const masked = stage.body.split('\n'); 16 for (const [i, line] of source.split('\n').entries()) { 17 if (line !== masked[i] && line.trimStart().startsWith('!import ') && line.includes(manifest)) { 18 range = { start: offset, end: offset + line.length }; 19 break; 20 } 21 offset += line.length + 1; 22 } 23 return { source, statement, existing: { alias: existing[1], ...range } }; 24 } 25 if (stage.imports.some(([, name]) => name === alias)) 26 throw new Error(`Namespace ${alias} is already used by another import.`); 27 const next = statement + source; 28 sourceText(next); 29 return { source: next, statement }; 30 }