@open-mercato/cli 0.6.6-develop.6089.1.a7ed560528 → 0.6.6-develop.6121.1.5797a901b3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,8 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
1
3
  import { VariableDeclarationKind } from "ts-morph";
2
- import { scanModuleDir, SCAN_CONFIGS } from "../scanner.js";
4
+ import ts from "typescript";
5
+ import { resolveStandaloneSourceMirrorBase, scanModuleDir, SCAN_CONFIGS } from "../scanner.js";
3
6
  import {
4
7
  arrayLiteral,
5
8
  arrowFunction,
@@ -11,6 +14,132 @@ import {
11
14
  writeValue
12
15
  } from "../ast/index.js";
13
16
  import { dynamicImportExpression, emptyObject, namespaceFallback, namespaceImportSpec, renderGeneratedTsSource } from "./shared.js";
17
+ function unwrapExpression(expression) {
18
+ let current = expression;
19
+ while (ts.isParenthesizedExpression(current) || ts.isAsExpression(current) || ts.isTypeAssertionExpression(current) || ts.isSatisfiesExpression(current)) {
20
+ current = current.expression;
21
+ }
22
+ return current;
23
+ }
24
+ function getPropertyNameText(name) {
25
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNoSubstitutionTemplateLiteral(name)) {
26
+ return name.text;
27
+ }
28
+ return null;
29
+ }
30
+ function collectObjectDeclarations(sourceFile) {
31
+ const declarations = /* @__PURE__ */ new Map();
32
+ for (const statement of sourceFile.statements) {
33
+ if (!ts.isVariableStatement(statement)) continue;
34
+ for (const declaration of statement.declarationList.declarations) {
35
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue;
36
+ const initializer = unwrapExpression(declaration.initializer);
37
+ if (ts.isObjectLiteralExpression(initializer)) {
38
+ declarations.set(declaration.name.text, initializer);
39
+ }
40
+ }
41
+ }
42
+ return declarations;
43
+ }
44
+ function resolveObjectExpression(expression, declarations) {
45
+ const unwrapped = unwrapExpression(expression);
46
+ if (ts.isObjectLiteralExpression(unwrapped)) return unwrapped;
47
+ if (ts.isIdentifier(unwrapped)) return declarations.get(unwrapped.text) ?? null;
48
+ return null;
49
+ }
50
+ function getObjectPropertyExpression(object, propertyName) {
51
+ for (const property of object.properties) {
52
+ if (!ts.isPropertyAssignment(property)) continue;
53
+ if (getPropertyNameText(property.name) === propertyName) return property.initializer;
54
+ }
55
+ return null;
56
+ }
57
+ function getStringPropertyValue(object, propertyName) {
58
+ const expression = getObjectPropertyExpression(object, propertyName);
59
+ if (!expression) return null;
60
+ const unwrapped = unwrapExpression(expression);
61
+ if (ts.isStringLiteral(unwrapped) || ts.isNoSubstitutionTemplateLiteral(unwrapped)) return unwrapped.text;
62
+ return null;
63
+ }
64
+ function extractMetadataId(object) {
65
+ const metadata = getObjectPropertyExpression(object, "metadata");
66
+ if (!metadata) return null;
67
+ const unwrapped = unwrapExpression(metadata);
68
+ if (!ts.isObjectLiteralExpression(unwrapped)) return null;
69
+ return getStringPropertyValue(unwrapped, "id");
70
+ }
71
+ function extractWidgetMetadataId(absolutePath) {
72
+ try {
73
+ const source = fs.readFileSync(absolutePath, "utf8");
74
+ const sourceFile = ts.createSourceFile(absolutePath, source, ts.ScriptTarget.Latest, true);
75
+ const declarations = collectObjectDeclarations(sourceFile);
76
+ for (const statement of sourceFile.statements) {
77
+ if (!ts.isExportAssignment(statement) || statement.isExportEquals) continue;
78
+ const object = resolveObjectExpression(statement.expression, declarations);
79
+ if (!object) continue;
80
+ const id = extractMetadataId(object);
81
+ if (id) return id;
82
+ }
83
+ for (const object of declarations.values()) {
84
+ const id = extractMetadataId(object);
85
+ if (id) return id;
86
+ }
87
+ } catch {
88
+ return null;
89
+ }
90
+ return null;
91
+ }
92
+ function collectWidgetIdsFromTableValue(value, declarations, ids) {
93
+ const expression = unwrapExpression(value);
94
+ if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) {
95
+ ids.add(expression.text);
96
+ return;
97
+ }
98
+ if (ts.isArrayLiteralExpression(expression)) {
99
+ for (const element of expression.elements) {
100
+ if (ts.isExpression(element)) collectWidgetIdsFromTableValue(element, declarations, ids);
101
+ }
102
+ return;
103
+ }
104
+ const object = resolveObjectExpression(expression, declarations);
105
+ if (!object) return;
106
+ const widgetId = getStringPropertyValue(object, "widgetId");
107
+ if (widgetId) ids.add(widgetId);
108
+ }
109
+ function extractInjectionTableWidgetIds(absolutePath) {
110
+ try {
111
+ const source = fs.readFileSync(absolutePath, "utf8");
112
+ const sourceFile = ts.createSourceFile(absolutePath, source, ts.ScriptTarget.Latest, true);
113
+ const declarations = collectObjectDeclarations(sourceFile);
114
+ let tableObject = null;
115
+ for (const statement of sourceFile.statements) {
116
+ if (ts.isVariableStatement(statement)) {
117
+ for (const declaration of statement.declarationList.declarations) {
118
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== "injectionTable" || !declaration.initializer) {
119
+ continue;
120
+ }
121
+ tableObject = resolveObjectExpression(declaration.initializer, declarations);
122
+ }
123
+ }
124
+ if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
125
+ tableObject ??= resolveObjectExpression(statement.expression, declarations);
126
+ }
127
+ }
128
+ if (!tableObject) return [];
129
+ const ids = /* @__PURE__ */ new Set();
130
+ for (const property of tableObject.properties) {
131
+ if (!ts.isPropertyAssignment(property)) continue;
132
+ collectWidgetIdsFromTableValue(property.initializer, declarations, ids);
133
+ }
134
+ return Array.from(ids);
135
+ } catch {
136
+ return [];
137
+ }
138
+ }
139
+ function resolveScannedWidgetPath(roots, fromApp, relPath) {
140
+ const base = fromApp ? roots.appBase : resolveStandaloneSourceMirrorBase(roots.pkgBase) ?? roots.pkgBase;
141
+ return path.join(base, "widgets", "injection", ...relPath.split("/"));
142
+ }
14
143
  function createInjectionWidgetsExtension() {
15
144
  const widgetEntries = /* @__PURE__ */ new Map();
16
145
  const tableEntries = [];
@@ -19,6 +148,7 @@ function createInjectionWidgetsExtension() {
19
148
  outputFiles: ["injection-widgets.generated.ts", "injection-tables.generated.ts"],
20
149
  scanModule(ctx) {
21
150
  const files = scanModuleDir(ctx.roots, SCAN_CONFIGS.injectionWidgets);
151
+ const moduleWidgetEntries = [];
22
152
  for (const { relPath, fromApp } of files) {
23
153
  const segments = relPath.split("/");
24
154
  const file = segments.pop() ?? "";
@@ -31,14 +161,22 @@ function createInjectionWidgetsExtension() {
31
161
  moduleId: ctx.moduleId,
32
162
  key,
33
163
  source: fromApp ? "app" : "package",
34
- importPath
164
+ importPath,
165
+ widgetId: extractWidgetMetadataId(resolveScannedWidgetPath(ctx.roots, fromApp, relPath)) ?? void 0
35
166
  };
36
- const existing = widgetEntries.get(key);
167
+ moduleWidgetEntries.push(entry);
168
+ }
169
+ const table = ctx.resolveModuleFile(ctx.roots, ctx.imps, "widgets/injection-table.ts");
170
+ const tableWidgetIds = table ? extractInjectionTableWidgetIds(table.absolutePath) : [];
171
+ if (moduleWidgetEntries.length === 1 && tableWidgetIds.length === 1 && !moduleWidgetEntries[0].widgetId) {
172
+ moduleWidgetEntries[0].widgetId = tableWidgetIds[0];
173
+ }
174
+ for (const entry of moduleWidgetEntries) {
175
+ const existing = widgetEntries.get(entry.key);
37
176
  if (!existing || existing.source !== "app" && entry.source === "app") {
38
- widgetEntries.set(key, entry);
177
+ widgetEntries.set(entry.key, entry);
39
178
  }
40
179
  }
41
- const table = ctx.resolveModuleFile(ctx.roots, ctx.imps, "widgets/injection-table.ts");
42
180
  if (table) {
43
181
  tableEntries.push({
44
182
  moduleId: ctx.moduleId,
@@ -53,6 +191,7 @@ function createInjectionWidgetsExtension() {
53
191
  { name: "moduleId", value: entry.moduleId },
54
192
  { name: "key", value: entry.key },
55
193
  { name: "source", value: entry.source },
194
+ ...entry.widgetId ? [{ name: "widgetId", value: entry.widgetId }] : [],
56
195
  {
57
196
  name: "loader",
58
197
  value: arrowFunction({
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "version": 3,
3
3
  "sources": ["../../../../src/lib/generators/extensions/injection-widgets.ts"],
4
- "sourcesContent": ["import { VariableDeclarationKind } from 'ts-morph'\nimport type { GeneratorExtension } from '../extension'\nimport { scanModuleDir, SCAN_CONFIGS } from '../scanner'\nimport {\n arrayLiteral,\n arrowFunction,\n identifier,\n methodCall,\n nullishCoalesce,\n objectLiteral,\n propertyAccess,\n writeValue,\n} from '../ast'\nimport { dynamicImportExpression, emptyObject, namespaceFallback, namespaceImportSpec, renderGeneratedTsSource } from './shared'\n\ntype InjectionWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n importPath: string\n}\n\ntype InjectionTableEntry = {\n moduleId: string\n importName: string\n importPath: string\n}\n\nexport function createInjectionWidgetsExtension(): GeneratorExtension {\n const widgetEntries = new Map<string, InjectionWidgetEntry>()\n const tableEntries: InjectionTableEntry[] = []\n\n return {\n id: 'registry.injection-widgets',\n outputFiles: ['injection-widgets.generated.ts', 'injection-tables.generated.ts'],\n scanModule(ctx) {\n const files = scanModuleDir(ctx.roots, SCAN_CONFIGS.injectionWidgets)\n for (const { relPath, fromApp } of files) {\n const segments = relPath.split('/')\n const file = segments.pop() ?? ''\n const base = file.replace(/\\.(t|j)sx?$/, '')\n const importPath = ctx.sanitizeGeneratedModuleSpecifier(\n `${fromApp ? ctx.imps.appBase : ctx.imps.pkgBase}/widgets/injection/${[...segments, base].join('/')}`\n )\n const key = [ctx.moduleId, ...segments, base].filter(Boolean).join(':')\n const entry: InjectionWidgetEntry = {\n moduleId: ctx.moduleId,\n key,\n source: fromApp ? 'app' : 'package',\n importPath,\n }\n const existing = widgetEntries.get(key)\n if (!existing || (existing.source !== 'app' && entry.source === 'app')) {\n widgetEntries.set(key, entry)\n }\n }\n\n const table = ctx.resolveModuleFile(ctx.roots, ctx.imps, 'widgets/injection-table.ts')\n if (table) {\n tableEntries.push({\n moduleId: ctx.moduleId,\n importName: `InjTable_${ctx.moduleId.replace(/[^a-zA-Z0-9_]/g, '_')}_${ctx.importIdRef.value++}`,\n importPath: ctx.sanitizeGeneratedModuleSpecifier(table.importPath),\n })\n }\n },\n generateOutput() {\n const widgetDecls = Array.from(widgetEntries.entries())\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([, entry]) =>\n objectLiteral([\n { name: 'moduleId', value: entry.moduleId },\n { name: 'key', value: entry.key },\n { name: 'source', value: entry.source },\n {\n name: 'loader',\n value: arrowFunction({\n body: methodCall(dynamicImportExpression(entry.importPath), 'then', [\n arrowFunction({\n parameters: ['mod'],\n body: nullishCoalesce([\n propertyAccess(identifier('mod'), 'default'),\n identifier('mod'),\n ]),\n }),\n ]),\n }),\n },\n ]),\n )\n\n const widgetsOutput = renderGeneratedTsSource({\n fileName: 'injection-widgets.generated.ts',\n imports: [\n { namedImports: [{ name: 'ModuleInjectionWidgetEntry', isTypeOnly: true }], moduleSpecifier: '@open-mercato/shared/modules/registry' },\n ],\n build(sourceFile) {\n sourceFile.addVariableStatement({\n declarationKind: VariableDeclarationKind.Const,\n isExported: true,\n declarations: [\n {\n name: 'injectionWidgetEntries',\n type: 'ModuleInjectionWidgetEntry[]',\n initializer: arrayLiteral(widgetDecls, writeValue),\n },\n ],\n })\n },\n })\n\n const tableImports = tableEntries.map((entry) => namespaceImportSpec(entry.importName, entry.importPath))\n const tableDecls = tableEntries.map((entry) =>\n objectLiteral([\n { name: 'moduleId', value: entry.moduleId },\n {\n name: 'table',\n value: namespaceFallback({\n importName: entry.importName,\n members: ['default', 'injectionTable'],\n fallback: emptyObject(),\n castType: 'ModuleInjectionTable',\n }),\n },\n ]),\n )\n const tablesOutput = renderGeneratedTsSource({\n fileName: 'injection-tables.generated.ts',\n imports: [\n { namedImports: [{ name: 'ModuleInjectionTable', isTypeOnly: true }], moduleSpecifier: '@open-mercato/shared/modules/widgets/injection' },\n ...tableImports,\n ],\n build(sourceFile) {\n sourceFile.addVariableStatement({\n declarationKind: VariableDeclarationKind.Const,\n isExported: true,\n declarations: [\n {\n name: 'injectionTables',\n type: 'Array<{ moduleId: string; table: ModuleInjectionTable }>',\n initializer: arrayLiteral(tableDecls, writeValue),\n },\n ],\n })\n },\n })\n\n return new Map([\n ['injection-widgets.generated.ts', widgetsOutput],\n ['injection-tables.generated.ts', tablesOutput],\n ])\n },\n }\n}\n"],
5
- "mappings": "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",
4
+ "sourcesContent": ["import fs from 'node:fs'\nimport path from 'node:path'\nimport { VariableDeclarationKind } from 'ts-morph'\nimport ts from 'typescript'\nimport type { GeneratorExtension } from '../extension'\nimport { resolveStandaloneSourceMirrorBase, scanModuleDir, SCAN_CONFIGS, type ModuleRoots } from '../scanner'\nimport {\n arrayLiteral,\n arrowFunction,\n identifier,\n methodCall,\n nullishCoalesce,\n objectLiteral,\n propertyAccess,\n writeValue,\n} from '../ast'\nimport { dynamicImportExpression, emptyObject, namespaceFallback, namespaceImportSpec, renderGeneratedTsSource } from './shared'\n\ntype InjectionWidgetEntry = {\n moduleId: string\n key: string\n source: 'app' | 'package'\n importPath: string\n widgetId?: string\n}\n\ntype InjectionTableEntry = {\n moduleId: string\n importName: string\n importPath: string\n}\n\nfunction unwrapExpression(expression: ts.Expression): ts.Expression {\n let current = expression\n while (\n ts.isParenthesizedExpression(current)\n || ts.isAsExpression(current)\n || ts.isTypeAssertionExpression(current)\n || ts.isSatisfiesExpression(current)\n ) {\n current = current.expression\n }\n return current\n}\n\nfunction getPropertyNameText(name: ts.PropertyName): string | null {\n if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNoSubstitutionTemplateLiteral(name)) {\n return name.text\n }\n return null\n}\n\nfunction collectObjectDeclarations(sourceFile: ts.SourceFile): Map<string, ts.ObjectLiteralExpression> {\n const declarations = new Map<string, ts.ObjectLiteralExpression>()\n for (const statement of sourceFile.statements) {\n if (!ts.isVariableStatement(statement)) continue\n for (const declaration of statement.declarationList.declarations) {\n if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue\n const initializer = unwrapExpression(declaration.initializer)\n if (ts.isObjectLiteralExpression(initializer)) {\n declarations.set(declaration.name.text, initializer)\n }\n }\n }\n return declarations\n}\n\nfunction resolveObjectExpression(\n expression: ts.Expression,\n declarations: Map<string, ts.ObjectLiteralExpression>,\n): ts.ObjectLiteralExpression | null {\n const unwrapped = unwrapExpression(expression)\n if (ts.isObjectLiteralExpression(unwrapped)) return unwrapped\n if (ts.isIdentifier(unwrapped)) return declarations.get(unwrapped.text) ?? null\n return null\n}\n\nfunction getObjectPropertyExpression(object: ts.ObjectLiteralExpression, propertyName: string): ts.Expression | null {\n for (const property of object.properties) {\n if (!ts.isPropertyAssignment(property)) continue\n if (getPropertyNameText(property.name) === propertyName) return property.initializer\n }\n return null\n}\n\nfunction getStringPropertyValue(object: ts.ObjectLiteralExpression, propertyName: string): string | null {\n const expression = getObjectPropertyExpression(object, propertyName)\n if (!expression) return null\n const unwrapped = unwrapExpression(expression)\n if (ts.isStringLiteral(unwrapped) || ts.isNoSubstitutionTemplateLiteral(unwrapped)) return unwrapped.text\n return null\n}\n\nfunction extractMetadataId(object: ts.ObjectLiteralExpression): string | null {\n const metadata = getObjectPropertyExpression(object, 'metadata')\n if (!metadata) return null\n const unwrapped = unwrapExpression(metadata)\n if (!ts.isObjectLiteralExpression(unwrapped)) return null\n return getStringPropertyValue(unwrapped, 'id')\n}\n\nfunction extractWidgetMetadataId(absolutePath: string): string | null {\n try {\n const source = fs.readFileSync(absolutePath, 'utf8')\n const sourceFile = ts.createSourceFile(absolutePath, source, ts.ScriptTarget.Latest, true)\n const declarations = collectObjectDeclarations(sourceFile)\n\n for (const statement of sourceFile.statements) {\n if (!ts.isExportAssignment(statement) || statement.isExportEquals) continue\n const object = resolveObjectExpression(statement.expression, declarations)\n if (!object) continue\n const id = extractMetadataId(object)\n if (id) return id\n }\n\n for (const object of declarations.values()) {\n const id = extractMetadataId(object)\n if (id) return id\n }\n } catch {\n return null\n }\n return null\n}\n\nfunction collectWidgetIdsFromTableValue(\n value: ts.Expression,\n declarations: Map<string, ts.ObjectLiteralExpression>,\n ids: Set<string>,\n) {\n const expression = unwrapExpression(value)\n if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) {\n ids.add(expression.text)\n return\n }\n if (ts.isArrayLiteralExpression(expression)) {\n for (const element of expression.elements) {\n if (ts.isExpression(element)) collectWidgetIdsFromTableValue(element, declarations, ids)\n }\n return\n }\n const object = resolveObjectExpression(expression, declarations)\n if (!object) return\n const widgetId = getStringPropertyValue(object, 'widgetId')\n if (widgetId) ids.add(widgetId)\n}\n\nfunction extractInjectionTableWidgetIds(absolutePath: string): string[] {\n try {\n const source = fs.readFileSync(absolutePath, 'utf8')\n const sourceFile = ts.createSourceFile(absolutePath, source, ts.ScriptTarget.Latest, true)\n const declarations = collectObjectDeclarations(sourceFile)\n let tableObject: ts.ObjectLiteralExpression | null = null\n\n for (const statement of sourceFile.statements) {\n if (ts.isVariableStatement(statement)) {\n for (const declaration of statement.declarationList.declarations) {\n if (!ts.isIdentifier(declaration.name) || declaration.name.text !== 'injectionTable' || !declaration.initializer) {\n continue\n }\n tableObject = resolveObjectExpression(declaration.initializer, declarations)\n }\n }\n if (ts.isExportAssignment(statement) && !statement.isExportEquals) {\n tableObject ??= resolveObjectExpression(statement.expression, declarations)\n }\n }\n\n if (!tableObject) return []\n const ids = new Set<string>()\n for (const property of tableObject.properties) {\n if (!ts.isPropertyAssignment(property)) continue\n collectWidgetIdsFromTableValue(property.initializer, declarations, ids)\n }\n return Array.from(ids)\n } catch {\n return []\n }\n}\n\nfunction resolveScannedWidgetPath(roots: ModuleRoots, fromApp: boolean, relPath: string): string {\n const base = fromApp ? roots.appBase : (resolveStandaloneSourceMirrorBase(roots.pkgBase) ?? roots.pkgBase)\n return path.join(base, 'widgets', 'injection', ...relPath.split('/'))\n}\n\nexport function createInjectionWidgetsExtension(): GeneratorExtension {\n const widgetEntries = new Map<string, InjectionWidgetEntry>()\n const tableEntries: InjectionTableEntry[] = []\n\n return {\n id: 'registry.injection-widgets',\n outputFiles: ['injection-widgets.generated.ts', 'injection-tables.generated.ts'],\n scanModule(ctx) {\n const files = scanModuleDir(ctx.roots, SCAN_CONFIGS.injectionWidgets)\n const moduleWidgetEntries: InjectionWidgetEntry[] = []\n for (const { relPath, fromApp } of files) {\n const segments = relPath.split('/')\n const file = segments.pop() ?? ''\n const base = file.replace(/\\.(t|j)sx?$/, '')\n const importPath = ctx.sanitizeGeneratedModuleSpecifier(\n `${fromApp ? ctx.imps.appBase : ctx.imps.pkgBase}/widgets/injection/${[...segments, base].join('/')}`\n )\n const key = [ctx.moduleId, ...segments, base].filter(Boolean).join(':')\n const entry: InjectionWidgetEntry = {\n moduleId: ctx.moduleId,\n key,\n source: fromApp ? 'app' : 'package',\n importPath,\n widgetId: extractWidgetMetadataId(resolveScannedWidgetPath(ctx.roots, fromApp, relPath)) ?? undefined,\n }\n moduleWidgetEntries.push(entry)\n }\n\n const table = ctx.resolveModuleFile(ctx.roots, ctx.imps, 'widgets/injection-table.ts')\n const tableWidgetIds = table ? extractInjectionTableWidgetIds(table.absolutePath) : []\n if (moduleWidgetEntries.length === 1 && tableWidgetIds.length === 1 && !moduleWidgetEntries[0].widgetId) {\n moduleWidgetEntries[0].widgetId = tableWidgetIds[0]\n }\n\n for (const entry of moduleWidgetEntries) {\n const existing = widgetEntries.get(entry.key)\n if (!existing || (existing.source !== 'app' && entry.source === 'app')) {\n widgetEntries.set(entry.key, entry)\n }\n }\n\n if (table) {\n tableEntries.push({\n moduleId: ctx.moduleId,\n importName: `InjTable_${ctx.moduleId.replace(/[^a-zA-Z0-9_]/g, '_')}_${ctx.importIdRef.value++}`,\n importPath: ctx.sanitizeGeneratedModuleSpecifier(table.importPath),\n })\n }\n },\n generateOutput() {\n const widgetDecls = Array.from(widgetEntries.entries())\n .sort(([left], [right]) => left.localeCompare(right))\n .map(([, entry]) =>\n objectLiteral([\n { name: 'moduleId', value: entry.moduleId },\n { name: 'key', value: entry.key },\n { name: 'source', value: entry.source },\n ...(entry.widgetId ? [{ name: 'widgetId', value: entry.widgetId }] : []),\n {\n name: 'loader',\n value: arrowFunction({\n body: methodCall(dynamicImportExpression(entry.importPath), 'then', [\n arrowFunction({\n parameters: ['mod'],\n body: nullishCoalesce([\n propertyAccess(identifier('mod'), 'default'),\n identifier('mod'),\n ]),\n }),\n ]),\n }),\n },\n ]),\n )\n\n const widgetsOutput = renderGeneratedTsSource({\n fileName: 'injection-widgets.generated.ts',\n imports: [\n { namedImports: [{ name: 'ModuleInjectionWidgetEntry', isTypeOnly: true }], moduleSpecifier: '@open-mercato/shared/modules/registry' },\n ],\n build(sourceFile) {\n sourceFile.addVariableStatement({\n declarationKind: VariableDeclarationKind.Const,\n isExported: true,\n declarations: [\n {\n name: 'injectionWidgetEntries',\n type: 'ModuleInjectionWidgetEntry[]',\n initializer: arrayLiteral(widgetDecls, writeValue),\n },\n ],\n })\n },\n })\n\n const tableImports = tableEntries.map((entry) => namespaceImportSpec(entry.importName, entry.importPath))\n const tableDecls = tableEntries.map((entry) =>\n objectLiteral([\n { name: 'moduleId', value: entry.moduleId },\n {\n name: 'table',\n value: namespaceFallback({\n importName: entry.importName,\n members: ['default', 'injectionTable'],\n fallback: emptyObject(),\n castType: 'ModuleInjectionTable',\n }),\n },\n ]),\n )\n const tablesOutput = renderGeneratedTsSource({\n fileName: 'injection-tables.generated.ts',\n imports: [\n { namedImports: [{ name: 'ModuleInjectionTable', isTypeOnly: true }], moduleSpecifier: '@open-mercato/shared/modules/widgets/injection' },\n ...tableImports,\n ],\n build(sourceFile) {\n sourceFile.addVariableStatement({\n declarationKind: VariableDeclarationKind.Const,\n isExported: true,\n declarations: [\n {\n name: 'injectionTables',\n type: 'Array<{ moduleId: string; table: ModuleInjectionTable }>',\n initializer: arrayLiteral(tableDecls, writeValue),\n },\n ],\n })\n },\n })\n\n return new Map([\n ['injection-widgets.generated.ts', widgetsOutput],\n ['injection-tables.generated.ts', tablesOutput],\n ])\n },\n }\n}\n"],
5
+ "mappings": "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",
6
6
  "names": []
7
7
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@open-mercato/cli",
3
- "version": "0.6.6-develop.6089.1.a7ed560528",
3
+ "version": "0.6.6-develop.6121.1.5797a901b3",
4
4
  "license": "MIT",
5
5
  "type": "module",
6
6
  "main": "./dist/index.js",
@@ -60,8 +60,8 @@
60
60
  "@mikro-orm/decorators": "^7.1.4",
61
61
  "@mikro-orm/migrations": "^7.1.4",
62
62
  "@mikro-orm/postgresql": "^7.1.4",
63
- "@open-mercato/queue": "0.6.6-develop.6089.1.a7ed560528",
64
- "@open-mercato/shared": "0.6.6-develop.6089.1.a7ed560528",
63
+ "@open-mercato/queue": "0.6.6-develop.6121.1.5797a901b3",
64
+ "@open-mercato/shared": "0.6.6-develop.6121.1.5797a901b3",
65
65
  "cross-spawn": "^7.0.6",
66
66
  "pg": "8.21.0",
67
67
  "semver": "^7.8.4",
@@ -71,10 +71,10 @@
71
71
  "typescript": "^6.0.3"
72
72
  },
73
73
  "peerDependencies": {
74
- "@open-mercato/shared": "0.6.6-develop.6089.1.a7ed560528"
74
+ "@open-mercato/shared": "0.6.6-develop.6121.1.5797a901b3"
75
75
  },
76
76
  "devDependencies": {
77
- "@open-mercato/shared": "0.6.6-develop.6089.1.a7ed560528",
77
+ "@open-mercato/shared": "0.6.6-develop.6121.1.5797a901b3",
78
78
  "@types/jest": "^30.0.0",
79
79
  "jest": "^30.4.2",
80
80
  "ts-jest": "^29.4.11"
@@ -1091,6 +1091,10 @@ describe('injection-widgets.generated.ts', () => {
1091
1091
  expect(content).toContain('moduleId: "orders"')
1092
1092
  expect(content).toContain('key: "orders:sidebar:widget"')
1093
1093
  })
1094
+
1095
+ it('emits the injection table widgetId as an optional lookup hint', () => {
1096
+ expect(content).toContain('widgetId: "orders.sidebar"')
1097
+ })
1094
1098
  })
1095
1099
 
1096
1100
  describe('injection-tables.generated.ts', () => {
@@ -1,6 +1,9 @@
1
+ import fs from 'node:fs'
2
+ import path from 'node:path'
1
3
  import { VariableDeclarationKind } from 'ts-morph'
4
+ import ts from 'typescript'
2
5
  import type { GeneratorExtension } from '../extension'
3
- import { scanModuleDir, SCAN_CONFIGS } from '../scanner'
6
+ import { resolveStandaloneSourceMirrorBase, scanModuleDir, SCAN_CONFIGS, type ModuleRoots } from '../scanner'
4
7
  import {
5
8
  arrayLiteral,
6
9
  arrowFunction,
@@ -18,6 +21,7 @@ type InjectionWidgetEntry = {
18
21
  key: string
19
22
  source: 'app' | 'package'
20
23
  importPath: string
24
+ widgetId?: string
21
25
  }
22
26
 
23
27
  type InjectionTableEntry = {
@@ -26,6 +30,159 @@ type InjectionTableEntry = {
26
30
  importPath: string
27
31
  }
28
32
 
33
+ function unwrapExpression(expression: ts.Expression): ts.Expression {
34
+ let current = expression
35
+ while (
36
+ ts.isParenthesizedExpression(current)
37
+ || ts.isAsExpression(current)
38
+ || ts.isTypeAssertionExpression(current)
39
+ || ts.isSatisfiesExpression(current)
40
+ ) {
41
+ current = current.expression
42
+ }
43
+ return current
44
+ }
45
+
46
+ function getPropertyNameText(name: ts.PropertyName): string | null {
47
+ if (ts.isIdentifier(name) || ts.isStringLiteral(name) || ts.isNoSubstitutionTemplateLiteral(name)) {
48
+ return name.text
49
+ }
50
+ return null
51
+ }
52
+
53
+ function collectObjectDeclarations(sourceFile: ts.SourceFile): Map<string, ts.ObjectLiteralExpression> {
54
+ const declarations = new Map<string, ts.ObjectLiteralExpression>()
55
+ for (const statement of sourceFile.statements) {
56
+ if (!ts.isVariableStatement(statement)) continue
57
+ for (const declaration of statement.declarationList.declarations) {
58
+ if (!ts.isIdentifier(declaration.name) || !declaration.initializer) continue
59
+ const initializer = unwrapExpression(declaration.initializer)
60
+ if (ts.isObjectLiteralExpression(initializer)) {
61
+ declarations.set(declaration.name.text, initializer)
62
+ }
63
+ }
64
+ }
65
+ return declarations
66
+ }
67
+
68
+ function resolveObjectExpression(
69
+ expression: ts.Expression,
70
+ declarations: Map<string, ts.ObjectLiteralExpression>,
71
+ ): ts.ObjectLiteralExpression | null {
72
+ const unwrapped = unwrapExpression(expression)
73
+ if (ts.isObjectLiteralExpression(unwrapped)) return unwrapped
74
+ if (ts.isIdentifier(unwrapped)) return declarations.get(unwrapped.text) ?? null
75
+ return null
76
+ }
77
+
78
+ function getObjectPropertyExpression(object: ts.ObjectLiteralExpression, propertyName: string): ts.Expression | null {
79
+ for (const property of object.properties) {
80
+ if (!ts.isPropertyAssignment(property)) continue
81
+ if (getPropertyNameText(property.name) === propertyName) return property.initializer
82
+ }
83
+ return null
84
+ }
85
+
86
+ function getStringPropertyValue(object: ts.ObjectLiteralExpression, propertyName: string): string | null {
87
+ const expression = getObjectPropertyExpression(object, propertyName)
88
+ if (!expression) return null
89
+ const unwrapped = unwrapExpression(expression)
90
+ if (ts.isStringLiteral(unwrapped) || ts.isNoSubstitutionTemplateLiteral(unwrapped)) return unwrapped.text
91
+ return null
92
+ }
93
+
94
+ function extractMetadataId(object: ts.ObjectLiteralExpression): string | null {
95
+ const metadata = getObjectPropertyExpression(object, 'metadata')
96
+ if (!metadata) return null
97
+ const unwrapped = unwrapExpression(metadata)
98
+ if (!ts.isObjectLiteralExpression(unwrapped)) return null
99
+ return getStringPropertyValue(unwrapped, 'id')
100
+ }
101
+
102
+ function extractWidgetMetadataId(absolutePath: string): string | null {
103
+ try {
104
+ const source = fs.readFileSync(absolutePath, 'utf8')
105
+ const sourceFile = ts.createSourceFile(absolutePath, source, ts.ScriptTarget.Latest, true)
106
+ const declarations = collectObjectDeclarations(sourceFile)
107
+
108
+ for (const statement of sourceFile.statements) {
109
+ if (!ts.isExportAssignment(statement) || statement.isExportEquals) continue
110
+ const object = resolveObjectExpression(statement.expression, declarations)
111
+ if (!object) continue
112
+ const id = extractMetadataId(object)
113
+ if (id) return id
114
+ }
115
+
116
+ for (const object of declarations.values()) {
117
+ const id = extractMetadataId(object)
118
+ if (id) return id
119
+ }
120
+ } catch {
121
+ return null
122
+ }
123
+ return null
124
+ }
125
+
126
+ function collectWidgetIdsFromTableValue(
127
+ value: ts.Expression,
128
+ declarations: Map<string, ts.ObjectLiteralExpression>,
129
+ ids: Set<string>,
130
+ ) {
131
+ const expression = unwrapExpression(value)
132
+ if (ts.isStringLiteral(expression) || ts.isNoSubstitutionTemplateLiteral(expression)) {
133
+ ids.add(expression.text)
134
+ return
135
+ }
136
+ if (ts.isArrayLiteralExpression(expression)) {
137
+ for (const element of expression.elements) {
138
+ if (ts.isExpression(element)) collectWidgetIdsFromTableValue(element, declarations, ids)
139
+ }
140
+ return
141
+ }
142
+ const object = resolveObjectExpression(expression, declarations)
143
+ if (!object) return
144
+ const widgetId = getStringPropertyValue(object, 'widgetId')
145
+ if (widgetId) ids.add(widgetId)
146
+ }
147
+
148
+ function extractInjectionTableWidgetIds(absolutePath: string): string[] {
149
+ try {
150
+ const source = fs.readFileSync(absolutePath, 'utf8')
151
+ const sourceFile = ts.createSourceFile(absolutePath, source, ts.ScriptTarget.Latest, true)
152
+ const declarations = collectObjectDeclarations(sourceFile)
153
+ let tableObject: ts.ObjectLiteralExpression | null = null
154
+
155
+ for (const statement of sourceFile.statements) {
156
+ if (ts.isVariableStatement(statement)) {
157
+ for (const declaration of statement.declarationList.declarations) {
158
+ if (!ts.isIdentifier(declaration.name) || declaration.name.text !== 'injectionTable' || !declaration.initializer) {
159
+ continue
160
+ }
161
+ tableObject = resolveObjectExpression(declaration.initializer, declarations)
162
+ }
163
+ }
164
+ if (ts.isExportAssignment(statement) && !statement.isExportEquals) {
165
+ tableObject ??= resolveObjectExpression(statement.expression, declarations)
166
+ }
167
+ }
168
+
169
+ if (!tableObject) return []
170
+ const ids = new Set<string>()
171
+ for (const property of tableObject.properties) {
172
+ if (!ts.isPropertyAssignment(property)) continue
173
+ collectWidgetIdsFromTableValue(property.initializer, declarations, ids)
174
+ }
175
+ return Array.from(ids)
176
+ } catch {
177
+ return []
178
+ }
179
+ }
180
+
181
+ function resolveScannedWidgetPath(roots: ModuleRoots, fromApp: boolean, relPath: string): string {
182
+ const base = fromApp ? roots.appBase : (resolveStandaloneSourceMirrorBase(roots.pkgBase) ?? roots.pkgBase)
183
+ return path.join(base, 'widgets', 'injection', ...relPath.split('/'))
184
+ }
185
+
29
186
  export function createInjectionWidgetsExtension(): GeneratorExtension {
30
187
  const widgetEntries = new Map<string, InjectionWidgetEntry>()
31
188
  const tableEntries: InjectionTableEntry[] = []
@@ -35,6 +192,7 @@ export function createInjectionWidgetsExtension(): GeneratorExtension {
35
192
  outputFiles: ['injection-widgets.generated.ts', 'injection-tables.generated.ts'],
36
193
  scanModule(ctx) {
37
194
  const files = scanModuleDir(ctx.roots, SCAN_CONFIGS.injectionWidgets)
195
+ const moduleWidgetEntries: InjectionWidgetEntry[] = []
38
196
  for (const { relPath, fromApp } of files) {
39
197
  const segments = relPath.split('/')
40
198
  const file = segments.pop() ?? ''
@@ -48,14 +206,24 @@ export function createInjectionWidgetsExtension(): GeneratorExtension {
48
206
  key,
49
207
  source: fromApp ? 'app' : 'package',
50
208
  importPath,
209
+ widgetId: extractWidgetMetadataId(resolveScannedWidgetPath(ctx.roots, fromApp, relPath)) ?? undefined,
51
210
  }
52
- const existing = widgetEntries.get(key)
211
+ moduleWidgetEntries.push(entry)
212
+ }
213
+
214
+ const table = ctx.resolveModuleFile(ctx.roots, ctx.imps, 'widgets/injection-table.ts')
215
+ const tableWidgetIds = table ? extractInjectionTableWidgetIds(table.absolutePath) : []
216
+ if (moduleWidgetEntries.length === 1 && tableWidgetIds.length === 1 && !moduleWidgetEntries[0].widgetId) {
217
+ moduleWidgetEntries[0].widgetId = tableWidgetIds[0]
218
+ }
219
+
220
+ for (const entry of moduleWidgetEntries) {
221
+ const existing = widgetEntries.get(entry.key)
53
222
  if (!existing || (existing.source !== 'app' && entry.source === 'app')) {
54
- widgetEntries.set(key, entry)
223
+ widgetEntries.set(entry.key, entry)
55
224
  }
56
225
  }
57
226
 
58
- const table = ctx.resolveModuleFile(ctx.roots, ctx.imps, 'widgets/injection-table.ts')
59
227
  if (table) {
60
228
  tableEntries.push({
61
229
  moduleId: ctx.moduleId,
@@ -72,6 +240,7 @@ export function createInjectionWidgetsExtension(): GeneratorExtension {
72
240
  { name: 'moduleId', value: entry.moduleId },
73
241
  { name: 'key', value: entry.key },
74
242
  { name: 'source', value: entry.source },
243
+ ...(entry.widgetId ? [{ name: 'widgetId', value: entry.widgetId }] : []),
75
244
  {
76
245
  name: 'loader',
77
246
  value: arrowFunction({