Support access to "external" schema module via some dotted path within a TypeScript module
This commit is contained in:
parent
ef6db841dd
commit
80dd3cd6b4
|
@ -91,13 +91,17 @@ export function runOnce(options: CommandLineArguments): CompilationResult {
|
||||||
|
|
||||||
const extensionEnv: M.Environment = options.module.map(arg => {
|
const extensionEnv: M.Environment = options.module.map(arg => {
|
||||||
const i = arg.indexOf('=');
|
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 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 {
|
return {
|
||||||
schema: null,
|
schema: null,
|
||||||
schemaModulePath: ns.split('.').map(Symbol.for),
|
schemaModulePath: ns.split('.').map(Symbol.for),
|
||||||
typescriptModulePath: path,
|
typescriptModulePath: path,
|
||||||
|
typescriptModuleExpr: expr,
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -120,6 +124,7 @@ export function runOnce(options: CommandLineArguments): CompilationResult {
|
||||||
schema: cc.schema,
|
schema: cc.schema,
|
||||||
schemaModulePath: cc.schemaPath,
|
schemaModulePath: cc.schemaPath,
|
||||||
typescriptModulePath: modulePathTo(c.outputFilePath, cc.outputFilePath),
|
typescriptModulePath: modulePathTo(c.outputFilePath, cc.outputFilePath),
|
||||||
|
typescriptModuleExpr: null,
|
||||||
})),
|
})),
|
||||||
];
|
];
|
||||||
fs.mkdirSync(path.dirname(c.outputFilePath), { recursive: true });
|
fs.mkdirSync(path.dirname(c.outputFilePath), { recursive: true });
|
||||||
|
|
|
@ -77,12 +77,12 @@ export function compile(
|
||||||
|
|
||||||
mod.definePreamble(
|
mod.definePreamble(
|
||||||
seq(`export const _imports = `, braces(... Array.from(mod.imports.values()).map(
|
seq(`export const _imports = `, braces(... Array.from(mod.imports.values()).map(
|
||||||
([modulePath, identifier, _path]) =>
|
([modulePath, identifier, _path, expr]) =>
|
||||||
seq(stringify(M.formatModulePath(modulePath)), ': ', identifier)))));
|
seq(stringify(M.formatModulePath(modulePath)), ': ', identifier, expr)))));
|
||||||
|
|
||||||
const f = new Formatter();
|
const f = new Formatter();
|
||||||
f.write(`import * as _ from ${JSON.stringify(options.preservesModule ?? '@preserves/core')};\n`);
|
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.write(`import * as ${identifier} from ${JSON.stringify(path)};\n`);
|
||||||
});
|
});
|
||||||
f.newline();
|
f.newline();
|
||||||
|
|
|
@ -31,7 +31,7 @@ export class ModuleContext {
|
||||||
readonly preamble: Item[] = [];
|
readonly preamble: Item[] = [];
|
||||||
readonly typedefs: Item[] = [];
|
readonly typedefs: Item[] = [];
|
||||||
readonly functiondefs: Item[] = [];
|
readonly functiondefs: Item[] = [];
|
||||||
readonly imports = new KeyedSet<[M.ModulePath, string, string]>();
|
readonly imports = new KeyedSet<[M.ModulePath, string, string, string]>();
|
||||||
|
|
||||||
constructor(
|
constructor(
|
||||||
env: M.Environment,
|
env: M.Environment,
|
||||||
|
@ -72,10 +72,10 @@ export class ModuleContext {
|
||||||
p.value._variant === 'SimplePattern' &&
|
p.value._variant === 'SimplePattern' &&
|
||||||
p.value.value._variant === 'Ref')
|
p.value.value._variant === 'Ref')
|
||||||
{
|
{
|
||||||
return this.lookup(
|
return this.lookup(p.value.value.value,
|
||||||
p.value.value.value,
|
(p, _t) => this.derefPattern(p, refCount + 1),
|
||||||
(p, _t) => this.derefPattern(p, refCount + 1),
|
((_modPath, _modId, _modFile, _modExpr, pp, _tt) =>
|
||||||
(_modPath, _modId, _modFile, pp, _tt) => this.derefPattern(pp ?? p, refCount + 1));
|
this.derefPattern(pp ?? p, refCount + 1)));
|
||||||
} else {
|
} else {
|
||||||
return p;
|
return p;
|
||||||
}
|
}
|
||||||
|
@ -97,15 +97,19 @@ export class ModuleContext {
|
||||||
return (ref) => this.lookup(
|
return (ref) => this.lookup(
|
||||||
ref,
|
ref,
|
||||||
(_p, _t) => Type.ref(ref.name.description!, ref),
|
(_p, _t) => Type.ref(ref.name.description!, ref),
|
||||||
(modPath, modId, modFile, _p, _t) => {
|
(modPath, modId, modFile, modExpr, _p, _t) => {
|
||||||
this.imports.add([modPath, modId, modFile]);
|
this.imports.add([modPath, modId, modFile, modExpr]);
|
||||||
return Type.ref(`${modId}.${ref.name.description!}`, ref);
|
return Type.ref(`${modId}${modExpr}.${ref.name.description!}`, ref);
|
||||||
},
|
},
|
||||||
modulePath);
|
modulePath);
|
||||||
}
|
}
|
||||||
|
|
||||||
lookupType(name: M.Ref, modulePath?: M.ModulePath): Type | null {
|
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;
|
return t ? t() : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -114,6 +118,7 @@ export class ModuleContext {
|
||||||
kOther: (modPath: M.ModulePath,
|
kOther: (modPath: M.ModulePath,
|
||||||
modId: string,
|
modId: string,
|
||||||
modFile: string,
|
modFile: string,
|
||||||
|
modExpr: string,
|
||||||
p: M.Definition | null,
|
p: M.Definition | null,
|
||||||
t: (() => Type) | null) => R,
|
t: (() => Type) | null) => R,
|
||||||
modulePath?: M.ModulePath)
|
modulePath?: M.ModulePath)
|
||||||
|
@ -122,15 +127,26 @@ export class ModuleContext {
|
||||||
|
|
||||||
for (const e of this.env) {
|
for (const e of this.env) {
|
||||||
if (is(e.schemaModulePath, soughtModule)) {
|
if (is(e.schemaModulePath, soughtModule)) {
|
||||||
|
const expr = (e.typescriptModuleExpr === null) ? '' : '.' + e.typescriptModuleExpr;
|
||||||
if (e.schema === null) {
|
if (e.schema === null) {
|
||||||
// It's an artificial module, not from a schema. Assume the identifier is present.
|
// 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 {
|
} else {
|
||||||
const p = e.schema.definitions.get(name.name);
|
const p = e.schema.definitions.get(name.name);
|
||||||
if (p !== void 0) {
|
if (p !== void 0) {
|
||||||
let t = () => typeForDefinition(this.resolver(soughtModule), p);
|
let t = () => typeForDefinition(this.resolver(soughtModule), p);
|
||||||
if (name.module.length) {
|
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 {
|
} else {
|
||||||
return kLocal(p, t);
|
return kLocal(p, t);
|
||||||
}
|
}
|
||||||
|
|
|
@ -186,9 +186,9 @@ export function converterForSimple(
|
||||||
return ctx.mod.lookup(
|
return ctx.mod.lookup(
|
||||||
p.value,
|
p.value,
|
||||||
(_p, _t) => [`${dest} = to${p.value.name.description!}(${src})`],
|
(_p, _t) => [`${dest} = to${p.value.name.description!}(${src})`],
|
||||||
(modPath, modId, modFile, _p, _t) => {
|
(modPath, modId, modFile, modExpr, _p, _t) => {
|
||||||
ctx.mod.imports.add([modPath, modId, modFile]);
|
ctx.mod.imports.add([modPath, modId, modFile, modExpr]);
|
||||||
return [`${dest} = ${modId}.to${p.value.name.description!}(${src})`];
|
return [`${dest} = ${modId}${modExpr}.to${p.value.name.description!}(${src})`];
|
||||||
});
|
});
|
||||||
default:
|
default:
|
||||||
((_p: never) => {})(p);
|
((_p: never) => {})(p);
|
||||||
|
|
|
@ -68,9 +68,9 @@ function unconverterFor(ctx: FunctionContext, p: M.Pattern, src: string): Item {
|
||||||
return ctx.mod.lookup(
|
return ctx.mod.lookup(
|
||||||
p.value,
|
p.value,
|
||||||
(_p, _t) => `from${p.value.name.description!}${ctx.mod.genericArgs()}(${src})`,
|
(_p, _t) => `from${p.value.name.description!}${ctx.mod.genericArgs()}(${src})`,
|
||||||
(modPath, modId, modFile, _p, _t) => {
|
(modPath, modId, modFile, modExpr, _p, _t) => {
|
||||||
ctx.mod.imports.add([modPath, modId, modFile]);
|
ctx.mod.imports.add([modPath, modId, modFile, modExpr]);
|
||||||
return `${modId}.from${p.value.name.description!}${ctx.mod.genericArgs()}(${src})`;
|
return `${modId}${modExpr}.from${p.value.name.description!}${ctx.mod.genericArgs()}(${src})`;
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
})(p.value);
|
})(p.value);
|
||||||
|
|
|
@ -45,6 +45,7 @@ export const ORSYM = Symbol.for('/');
|
||||||
export type SchemaEnvEntry = {
|
export type SchemaEnvEntry = {
|
||||||
schemaModulePath: M.ModulePath,
|
schemaModulePath: M.ModulePath,
|
||||||
typescriptModulePath: string,
|
typescriptModulePath: string,
|
||||||
|
typescriptModuleExpr: string | null,
|
||||||
schema: M.Schema | null, // null means it's an artificial one, not corresponding to an input
|
schema: M.Schema | null, // null means it's an artificial one, not corresponding to an input
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue