diff --git a/implementations/javascript/packages/schema/src/bin/preserves-schema-ts.ts b/implementations/javascript/packages/schema/src/bin/preserves-schema-ts.ts index e686c9e..d69bbf5 100644 --- a/implementations/javascript/packages/schema/src/bin/preserves-schema-ts.ts +++ b/implementations/javascript/packages/schema/src/bin/preserves-schema-ts.ts @@ -91,13 +91,17 @@ export function runOnce(options: CommandLineArguments): CompilationResult { const extensionEnv: M.Environment = options.module.map(arg => { const i = arg.indexOf('='); - if (i === -1) throw new Error(`--module argument must be Namespace=path: ${arg}`); + if (i === -1) throw new Error(`--module argument must be Namespace=path or Namespace=path:expr; got ${arg}`); const ns = arg.slice(0, i); - const path = arg.slice(i + 1); + const pathAndExpr = arg.slice(i + 1); + const j = pathAndExpr.lastIndexOf(':'); + const path = (j === -1) ? pathAndExpr : pathAndExpr.slice(0, j); + const expr = (j === -1) ? null : pathAndExpr.slice(j + 1); return { schema: null, schemaModulePath: ns.split('.').map(Symbol.for), typescriptModulePath: path, + typescriptModuleExpr: expr, }; }); @@ -120,6 +124,7 @@ export function runOnce(options: CommandLineArguments): CompilationResult { schema: cc.schema, schemaModulePath: cc.schemaPath, typescriptModulePath: modulePathTo(c.outputFilePath, cc.outputFilePath), + typescriptModuleExpr: null, })), ]; fs.mkdirSync(path.dirname(c.outputFilePath), { recursive: true }); diff --git a/implementations/javascript/packages/schema/src/compiler.ts b/implementations/javascript/packages/schema/src/compiler.ts index 2e14d65..7a9edc4 100644 --- a/implementations/javascript/packages/schema/src/compiler.ts +++ b/implementations/javascript/packages/schema/src/compiler.ts @@ -77,12 +77,12 @@ export function compile( mod.definePreamble( seq(`export const _imports = `, braces(... Array.from(mod.imports.values()).map( - ([modulePath, identifier, _path]) => - seq(stringify(M.formatModulePath(modulePath)), ': ', identifier))))); + ([modulePath, identifier, _path, expr]) => + seq(stringify(M.formatModulePath(modulePath)), ': ', identifier, expr))))); const f = new Formatter(); f.write(`import * as _ from ${JSON.stringify(options.preservesModule ?? '@preserves/core')};\n`); - mod.imports.forEach(([_modulePath, identifier, path]) => { + mod.imports.forEach(([_modulePath, identifier, path, _expr]) => { f.write(`import * as ${identifier} from ${JSON.stringify(path)};\n`); }); f.newline(); diff --git a/implementations/javascript/packages/schema/src/compiler/context.ts b/implementations/javascript/packages/schema/src/compiler/context.ts index abe26cc..1bdccf6 100644 --- a/implementations/javascript/packages/schema/src/compiler/context.ts +++ b/implementations/javascript/packages/schema/src/compiler/context.ts @@ -31,7 +31,7 @@ export class ModuleContext { readonly preamble: Item[] = []; readonly typedefs: Item[] = []; readonly functiondefs: Item[] = []; - readonly imports = new KeyedSet<[M.ModulePath, string, string]>(); + readonly imports = new KeyedSet<[M.ModulePath, string, string, string]>(); constructor( env: M.Environment, @@ -72,10 +72,10 @@ export class ModuleContext { p.value._variant === 'SimplePattern' && p.value.value._variant === 'Ref') { - return this.lookup( - p.value.value.value, - (p, _t) => this.derefPattern(p, refCount + 1), - (_modPath, _modId, _modFile, pp, _tt) => this.derefPattern(pp ?? p, refCount + 1)); + return this.lookup(p.value.value.value, + (p, _t) => this.derefPattern(p, refCount + 1), + ((_modPath, _modId, _modFile, _modExpr, pp, _tt) => + this.derefPattern(pp ?? p, refCount + 1))); } else { return p; } @@ -97,15 +97,19 @@ export class ModuleContext { return (ref) => this.lookup( ref, (_p, _t) => Type.ref(ref.name.description!, ref), - (modPath, modId, modFile, _p, _t) => { - this.imports.add([modPath, modId, modFile]); - return Type.ref(`${modId}.${ref.name.description!}`, ref); + (modPath, modId, modFile, modExpr, _p, _t) => { + this.imports.add([modPath, modId, modFile, modExpr]); + return Type.ref(`${modId}${modExpr}.${ref.name.description!}`, ref); }, modulePath); } lookupType(name: M.Ref, modulePath?: M.ModulePath): Type | null { - const t = this.lookup(name, (_p, t) => t, (_modPath, _modId, _modFile, _p, t) => t, modulePath); + const t = this.lookup( + name, + (_p, t) => t, + (_modPath, _modId, _modFile, _modExpr, _p, t) => t, + modulePath); return t ? t() : null; } @@ -114,6 +118,7 @@ export class ModuleContext { kOther: (modPath: M.ModulePath, modId: string, modFile: string, + modExpr: string, p: M.Definition | null, t: (() => Type) | null) => R, modulePath?: M.ModulePath) @@ -122,15 +127,26 @@ export class ModuleContext { for (const e of this.env) { if (is(e.schemaModulePath, soughtModule)) { + const expr = (e.typescriptModuleExpr === null) ? '' : '.' + e.typescriptModuleExpr; if (e.schema === null) { // It's an artificial module, not from a schema. Assume the identifier is present. - return kOther(soughtModule, M.modsymFor(e), e.typescriptModulePath, null, null); + return kOther(soughtModule, + M.modsymFor(e), + e.typescriptModulePath, + expr, + null, + null); } else { const p = e.schema.definitions.get(name.name); if (p !== void 0) { let t = () => typeForDefinition(this.resolver(soughtModule), p); if (name.module.length) { - return kOther(soughtModule, M.modsymFor(e), e.typescriptModulePath, p, t); + return kOther(soughtModule, + M.modsymFor(e), + e.typescriptModulePath, + expr, + p, + t); } else { return kLocal(p, t); } diff --git a/implementations/javascript/packages/schema/src/compiler/genconverter.ts b/implementations/javascript/packages/schema/src/compiler/genconverter.ts index 5a427ca..f4ff02a 100644 --- a/implementations/javascript/packages/schema/src/compiler/genconverter.ts +++ b/implementations/javascript/packages/schema/src/compiler/genconverter.ts @@ -186,9 +186,9 @@ export function converterForSimple( return ctx.mod.lookup( p.value, (_p, _t) => [`${dest} = to${p.value.name.description!}(${src})`], - (modPath, modId, modFile, _p, _t) => { - ctx.mod.imports.add([modPath, modId, modFile]); - return [`${dest} = ${modId}.to${p.value.name.description!}(${src})`]; + (modPath, modId, modFile, modExpr, _p, _t) => { + ctx.mod.imports.add([modPath, modId, modFile, modExpr]); + return [`${dest} = ${modId}${modExpr}.to${p.value.name.description!}(${src})`]; }); default: ((_p: never) => {})(p); diff --git a/implementations/javascript/packages/schema/src/compiler/genunconverter.ts b/implementations/javascript/packages/schema/src/compiler/genunconverter.ts index fd89548..08afa01 100644 --- a/implementations/javascript/packages/schema/src/compiler/genunconverter.ts +++ b/implementations/javascript/packages/schema/src/compiler/genunconverter.ts @@ -68,9 +68,9 @@ function unconverterFor(ctx: FunctionContext, p: M.Pattern, src: string): Item { return ctx.mod.lookup( p.value, (_p, _t) => `from${p.value.name.description!}${ctx.mod.genericArgs()}(${src})`, - (modPath, modId, modFile, _p, _t) => { - ctx.mod.imports.add([modPath, modId, modFile]); - return `${modId}.from${p.value.name.description!}${ctx.mod.genericArgs()}(${src})`; + (modPath, modId, modFile, modExpr, _p, _t) => { + ctx.mod.imports.add([modPath, modId, modFile, modExpr]); + return `${modId}${modExpr}.from${p.value.name.description!}${ctx.mod.genericArgs()}(${src})`; }); } })(p.value); diff --git a/implementations/javascript/packages/schema/src/meta.ts b/implementations/javascript/packages/schema/src/meta.ts index a4b762e..9219350 100644 --- a/implementations/javascript/packages/schema/src/meta.ts +++ b/implementations/javascript/packages/schema/src/meta.ts @@ -45,6 +45,7 @@ export const ORSYM = Symbol.for('/'); export type SchemaEnvEntry = { schemaModulePath: M.ModulePath, typescriptModulePath: string, + typescriptModuleExpr: string | null, schema: M.Schema | null, // null means it's an artificial one, not corresponding to an input };