@open-mercato/cli 0.10.1-develop.7990.1.82d0a183c5 → 0.10.1-develop.8004.1.a8a79ee43f
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.
- package/.turbo/turbo-build.log +1 -1
- package/build.mjs +1 -0
- package/dist/agentic/guides/module-facts.json +162 -162
- package/dist/agentic/guides/module-facts.v2.json +162 -162
- package/dist/agentic/guides/module-tool.md +21 -0
- package/dist/agentic/guides/modules/agent_orchestrator/index.md +1 -1
- package/dist/agentic/guides/modules/ai_assistant/index.md +1 -1
- package/dist/agentic/guides/modules/api_docs/index.md +1 -1
- package/dist/agentic/guides/modules/api_keys/index.md +1 -1
- package/dist/agentic/guides/modules/attachments/index.md +1 -1
- package/dist/agentic/guides/modules/audit_logs/index.md +1 -1
- package/dist/agentic/guides/modules/auth/index.md +1 -1
- package/dist/agentic/guides/modules/availability/index.md +1 -1
- package/dist/agentic/guides/modules/business_rules/index.md +1 -1
- package/dist/agentic/guides/modules/carrier_inpost/index.md +1 -1
- package/dist/agentic/guides/modules/cart/index.md +1 -1
- package/dist/agentic/guides/modules/catalog/index.md +1 -1
- package/dist/agentic/guides/modules/channel_apns/index.md +1 -1
- package/dist/agentic/guides/modules/channel_discord/index.md +1 -1
- package/dist/agentic/guides/modules/channel_expo/index.md +1 -1
- package/dist/agentic/guides/modules/channel_fcm/index.md +1 -1
- package/dist/agentic/guides/modules/channel_gmail/index.md +1 -1
- package/dist/agentic/guides/modules/channel_imap/index.md +1 -1
- package/dist/agentic/guides/modules/channel_resend/index.md +1 -1
- package/dist/agentic/guides/modules/channel_ses/index.md +1 -1
- package/dist/agentic/guides/modules/channel_smtp/index.md +1 -1
- package/dist/agentic/guides/modules/checkout/index.md +1 -1
- package/dist/agentic/guides/modules/communication_channels/index.md +1 -1
- package/dist/agentic/guides/modules/configs/index.md +1 -1
- package/dist/agentic/guides/modules/content/index.md +1 -1
- package/dist/agentic/guides/modules/currencies/index.md +1 -1
- package/dist/agentic/guides/modules/customer_accounts/index.md +1 -1
- package/dist/agentic/guides/modules/customer_groups/index.md +1 -1
- package/dist/agentic/guides/modules/customers/index.md +1 -1
- package/dist/agentic/guides/modules/dashboards/index.md +1 -1
- package/dist/agentic/guides/modules/data_sync/index.md +1 -1
- package/dist/agentic/guides/modules/design_system/index.md +1 -1
- package/dist/agentic/guides/modules/devices/index.md +1 -1
- package/dist/agentic/guides/modules/dictionaries/index.md +1 -1
- package/dist/agentic/guides/modules/directory/index.md +1 -1
- package/dist/agentic/guides/modules/documents/index.md +1 -1
- package/dist/agentic/guides/modules/ecommerce/index.md +1 -1
- package/dist/agentic/guides/modules/entities/index.md +1 -1
- package/dist/agentic/guides/modules/eudr/index.md +1 -1
- package/dist/agentic/guides/modules/events/index.md +1 -1
- package/dist/agentic/guides/modules/feature_toggles/index.md +1 -1
- package/dist/agentic/guides/modules/forms/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_autopay/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_stripe/index.md +1 -1
- package/dist/agentic/guides/modules/gateway_tpay/index.md +1 -1
- package/dist/agentic/guides/modules/generators/index.md +1 -1
- package/dist/agentic/guides/modules/inbox_ops/index.md +1 -1
- package/dist/agentic/guides/modules/integrations/index.md +1 -1
- package/dist/agentic/guides/modules/messages/index.md +1 -1
- package/dist/agentic/guides/modules/notifications/index.md +1 -1
- package/dist/agentic/guides/modules/onboarding/index.md +1 -1
- package/dist/agentic/guides/modules/payment_gateways/index.md +1 -1
- package/dist/agentic/guides/modules/perspectives/index.md +1 -1
- package/dist/agentic/guides/modules/phone_calls/index.md +1 -1
- package/dist/agentic/guides/modules/planner/index.md +1 -1
- package/dist/agentic/guides/modules/portal/index.md +1 -1
- package/dist/agentic/guides/modules/progress/index.md +1 -1
- package/dist/agentic/guides/modules/promotions/index.md +1 -1
- package/dist/agentic/guides/modules/push_notifications/index.md +1 -1
- package/dist/agentic/guides/modules/query_index/index.md +1 -1
- package/dist/agentic/guides/modules/record_locks/index.md +1 -1
- package/dist/agentic/guides/modules/resources/index.md +1 -1
- package/dist/agentic/guides/modules/sales/index.md +1 -1
- package/dist/agentic/guides/modules/scheduler/index.md +1 -1
- package/dist/agentic/guides/modules/search/index.md +1 -1
- package/dist/agentic/guides/modules/security/index.md +1 -1
- package/dist/agentic/guides/modules/seeds/index.md +1 -1
- package/dist/agentic/guides/modules/shipping_carriers/index.md +1 -1
- package/dist/agentic/guides/modules/sso/index.md +1 -1
- package/dist/agentic/guides/modules/staff/index.md +1 -1
- package/dist/agentic/guides/modules/storage_s3/index.md +1 -1
- package/dist/agentic/guides/modules/sync_akeneo/index.md +1 -1
- package/dist/agentic/guides/modules/sync_excel/index.md +1 -1
- package/dist/agentic/guides/modules/system_status_overlays/index.md +1 -1
- package/dist/agentic/guides/modules/telemetry/index.md +1 -1
- package/dist/agentic/guides/modules/tillio/index.md +1 -1
- package/dist/agentic/guides/modules/translations/index.md +1 -1
- package/dist/agentic/guides/modules/warranty_claims/index.md +1 -1
- package/dist/agentic/guides/modules/webhooks/index.md +1 -1
- package/dist/agentic/guides/modules/wms/index.md +1 -1
- package/dist/agentic/guides/modules/workflows/index.md +1 -1
- package/dist/agentic/guides/reference-module-facts.json +1 -1
- package/dist/agentic/guides/upstream/BACKWARD_COMPATIBILITY.md +4 -0
- package/dist/agentic/guides/upstream/manifest.json +2 -2
- package/dist/agentic/shared/ai/harness/cases.json +23 -9
- package/dist/agentic/shared/ai/harness/source-link-inventory.json +90 -4
- package/dist/i18n/de.json +4 -0
- package/dist/i18n/en.json +4 -0
- package/dist/i18n/es.json +4 -0
- package/dist/i18n/ko.json +4 -0
- package/dist/i18n/pl.json +4 -0
- package/dist/lib/__fixtures__/module-search/built-in.json +235 -0
- package/dist/lib/__fixtures__/module-search/catalog-parity.json +4076 -0
- package/dist/lib/__fixtures__/module-search/modules-index.json +1980 -0
- package/dist/lib/__fixtures__/official-module-package/package.json +33 -0
- package/dist/lib/__integration__/TC-INT-007.spec.js +207 -19
- package/dist/lib/__integration__/TC-INT-007.spec.js.map +2 -2
- package/dist/lib/__integration__/TC-INT-009.spec.js +267 -0
- package/dist/lib/__integration__/TC-INT-009.spec.js.map +7 -0
- package/dist/lib/eject.js +25 -9
- package/dist/lib/eject.js.map +2 -2
- package/dist/lib/generators/entity-ids.js +2 -0
- package/dist/lib/generators/entity-ids.js.map +2 -2
- package/dist/lib/generators/module-di.js +2 -0
- package/dist/lib/generators/module-di.js.map +2 -2
- package/dist/lib/generators/module-entities.js +2 -0
- package/dist/lib/generators/module-entities.js.map +2 -2
- package/dist/lib/generators/module-package-sources.js +6 -4
- package/dist/lib/generators/module-package-sources.js.map +2 -2
- package/dist/lib/generators/module-registry.js +32 -86
- package/dist/lib/generators/module-registry.js.map +2 -2
- package/dist/lib/generators/openapi.js +2 -0
- package/dist/lib/generators/openapi.js.map +2 -2
- package/dist/lib/generators/source-exports.js +73 -0
- package/dist/lib/generators/source-exports.js.map +7 -0
- package/dist/lib/init-secrets.js +33 -1
- package/dist/lib/init-secrets.js.map +2 -2
- package/dist/lib/init-summary.js +44 -0
- package/dist/lib/init-summary.js.map +7 -0
- package/dist/lib/module-github.js +98 -0
- package/dist/lib/module-github.js.map +7 -0
- package/dist/lib/module-identity.js +131 -0
- package/dist/lib/module-identity.js.map +7 -0
- package/dist/lib/module-inspection.js +124 -0
- package/dist/lib/module-inspection.js.map +7 -0
- package/dist/lib/module-install.js +150 -21
- package/dist/lib/module-install.js.map +3 -3
- package/dist/lib/module-package.js +205 -15
- package/dist/lib/module-package.js.map +2 -2
- package/dist/lib/modules-config.js +69 -30
- package/dist/lib/modules-config.js.map +2 -2
- package/dist/lib/resolver.js +33 -34
- package/dist/lib/resolver.js.map +2 -2
- package/dist/mercato.js +17 -37
- package/dist/mercato.js.map +2 -2
- package/package.json +7 -7
- package/src/__tests__/mercato.test.ts +71 -4
- package/src/i18n/de.json +4 -0
- package/src/i18n/en.json +4 -0
- package/src/i18n/es.json +4 -0
- package/src/i18n/ko.json +4 -0
- package/src/i18n/pl.json +4 -0
- package/src/lib/__fixtures__/official-module-package/dist/modules/test_package/widgets/injection/test/widget.js +1 -0
- package/src/lib/__integration__/TC-INT-007.spec.ts +219 -19
- package/src/lib/__integration__/TC-INT-009.spec.ts +279 -0
- package/src/lib/__tests__/eject.test.ts +98 -4
- package/src/lib/__tests__/init-secrets.test.ts +85 -1
- package/src/lib/__tests__/init-summary.test.ts +50 -0
- package/src/lib/__tests__/module-github.test.ts +108 -0
- package/src/lib/__tests__/module-identity.test.ts +144 -0
- package/src/lib/__tests__/module-inspection.test.ts +195 -0
- package/src/lib/__tests__/module-install.test.ts +399 -10
- package/src/lib/__tests__/module-package.test.ts +154 -0
- package/src/lib/__tests__/modules-config.test.ts +55 -0
- package/src/lib/__tests__/resolver.packages.test.ts +29 -0
- package/src/lib/eject.ts +28 -10
- package/src/lib/generators/__tests__/module-id-collisions.test.ts +146 -0
- package/src/lib/generators/__tests__/module-package-sources.test.ts +19 -2
- package/src/lib/generators/__tests__/module-requirements.test.ts +56 -0
- package/src/lib/generators/entity-ids.ts +2 -0
- package/src/lib/generators/module-di.ts +2 -0
- package/src/lib/generators/module-entities.ts +2 -0
- package/src/lib/generators/module-package-sources.ts +7 -5
- package/src/lib/generators/module-registry.ts +33 -115
- package/src/lib/generators/openapi.ts +3 -0
- package/src/lib/generators/source-exports.ts +93 -0
- package/src/lib/init-secrets.ts +37 -0
- package/src/lib/init-summary.ts +54 -0
- package/src/lib/module-github.ts +109 -0
- package/src/lib/module-identity.ts +145 -0
- package/src/lib/module-inspection.ts +133 -0
- package/src/lib/module-install.ts +170 -22
- package/src/lib/module-package.ts +243 -16
- package/src/lib/modules-config.ts +80 -32
- package/src/lib/resolver.ts +35 -39
- package/src/mercato.ts +18 -44
- package/src/types/cross-spawn.d.ts +2 -2
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import fs from 'node:fs'
|
|
2
2
|
import path from 'node:path'
|
|
3
3
|
import { createRequire } from 'node:module'
|
|
4
|
+
import ts from 'typescript-js'
|
|
4
5
|
import type { PackageResolver } from './resolver'
|
|
6
|
+
import { normalizeModuleLocator } from '@open-mercato/shared/modules/module-locators'
|
|
7
|
+
import { sourceHasDefaultExport, sourceExportedHttpMethods } from './generators/source-exports'
|
|
8
|
+
import { MODULE_CODE_EXTENSIONS, SCAN_CONFIGS, stripModuleCodeExtension, type ScanConfig } from './generators/scanner'
|
|
5
9
|
|
|
6
10
|
type PackageJsonRecord = {
|
|
7
11
|
name?: string
|
|
8
12
|
version?: string
|
|
9
13
|
dependencies?: Record<string, string>
|
|
10
14
|
peerDependencies?: Record<string, string>
|
|
15
|
+
exports?: unknown
|
|
11
16
|
}
|
|
12
17
|
|
|
13
18
|
export type ModulePackageMetadata = {
|
|
@@ -18,6 +23,8 @@ export type ModulePackageMetadata = {
|
|
|
18
23
|
export type ModuleInfoSnapshot = {
|
|
19
24
|
title?: string
|
|
20
25
|
description?: string
|
|
26
|
+
requires?: string[]
|
|
27
|
+
locator?: string
|
|
21
28
|
}
|
|
22
29
|
|
|
23
30
|
export type ValidatedOfficialModulePackage = {
|
|
@@ -54,20 +61,94 @@ function readPackageJson(packageJsonPath: string): PackageJsonRecord {
|
|
|
54
61
|
}
|
|
55
62
|
}
|
|
56
63
|
|
|
57
|
-
function
|
|
58
|
-
if (!
|
|
64
|
+
function readPropertyName(property: ts.ObjectLiteralElementLike): string | null {
|
|
65
|
+
if (!('name' in property) || !property.name) return null
|
|
66
|
+
return ts.isIdentifier(property.name) || ts.isStringLiteralLike(property.name)
|
|
67
|
+
? property.name.text
|
|
68
|
+
: null
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
function unwrapExpression(expression: ts.Expression): ts.Expression {
|
|
72
|
+
if (
|
|
73
|
+
ts.isAsExpression(expression)
|
|
74
|
+
|| ts.isTypeAssertionExpression(expression)
|
|
75
|
+
|| ts.isParenthesizedExpression(expression)
|
|
76
|
+
|| ts.isSatisfiesExpression(expression)
|
|
77
|
+
) {
|
|
78
|
+
return unwrapExpression(expression.expression)
|
|
79
|
+
}
|
|
80
|
+
return expression
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function findMetadataLiteral(source: string, indexPath: string): ts.ObjectLiteralExpression | null {
|
|
84
|
+
const sourceFile = ts.createSourceFile(indexPath, source, ts.ScriptTarget.Latest, true)
|
|
85
|
+
for (const statement of sourceFile.statements) {
|
|
86
|
+
if (!ts.isVariableStatement(statement)) continue
|
|
87
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
88
|
+
if (
|
|
89
|
+
!ts.isIdentifier(declaration.name)
|
|
90
|
+
|| declaration.name.text !== 'metadata'
|
|
91
|
+
|| !declaration.initializer
|
|
92
|
+
) continue
|
|
93
|
+
const initializer = unwrapExpression(declaration.initializer)
|
|
94
|
+
if (ts.isObjectLiteralExpression(initializer)) return initializer
|
|
95
|
+
}
|
|
96
|
+
}
|
|
97
|
+
return null
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function parseModuleInfo(indexPath: string, validateRequires = false): {
|
|
101
|
+
title?: string
|
|
102
|
+
description?: string
|
|
103
|
+
ejectable?: boolean
|
|
104
|
+
locator?: string
|
|
105
|
+
requires: string[]
|
|
106
|
+
} {
|
|
107
|
+
if (!fs.existsSync(indexPath)) return { requires: [] }
|
|
59
108
|
|
|
60
109
|
const source = fs.readFileSync(indexPath, 'utf8')
|
|
61
|
-
const
|
|
110
|
+
const metadata = findMetadataLiteral(source, indexPath)
|
|
111
|
+
const result: { title?: string; description?: string; ejectable?: boolean; locator?: string; requires: string[] } = {
|
|
112
|
+
requires: [],
|
|
113
|
+
}
|
|
114
|
+
if (!metadata) return result
|
|
62
115
|
|
|
63
|
-
const
|
|
64
|
-
|
|
116
|
+
for (const property of metadata.properties) {
|
|
117
|
+
const propertyName = readPropertyName(property)
|
|
118
|
+
if (!ts.isPropertyAssignment(property)) {
|
|
119
|
+
if (validateRequires && propertyName === 'requires') {
|
|
120
|
+
throw new Error(`Module descriptor at ${indexPath} must declare "requires" as a static string array.`)
|
|
121
|
+
}
|
|
122
|
+
continue
|
|
123
|
+
}
|
|
124
|
+
const initializer = unwrapExpression(property.initializer)
|
|
65
125
|
|
|
66
|
-
|
|
67
|
-
|
|
126
|
+
if ((propertyName === 'title' || propertyName === 'description' || propertyName === 'locator') && ts.isStringLiteralLike(initializer)) {
|
|
127
|
+
result[propertyName] = initializer.text
|
|
128
|
+
continue
|
|
129
|
+
}
|
|
68
130
|
|
|
69
|
-
|
|
70
|
-
|
|
131
|
+
if (propertyName === 'ejectable') {
|
|
132
|
+
if (initializer.kind === ts.SyntaxKind.TrueKeyword) result.ejectable = true
|
|
133
|
+
if (initializer.kind === ts.SyntaxKind.FalseKeyword) result.ejectable = false
|
|
134
|
+
continue
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
if (propertyName !== 'requires') continue
|
|
138
|
+
if (!ts.isArrayLiteralExpression(initializer)) {
|
|
139
|
+
if (validateRequires) {
|
|
140
|
+
throw new Error(`Module descriptor at ${indexPath} must declare "requires" as a static string array.`)
|
|
141
|
+
}
|
|
142
|
+
continue
|
|
143
|
+
}
|
|
144
|
+
if (initializer.elements.some((element) => !ts.isStringLiteralLike(element))) {
|
|
145
|
+
if (validateRequires) {
|
|
146
|
+
throw new Error(`Module descriptor at ${indexPath} must contain only string module ids in "requires".`)
|
|
147
|
+
}
|
|
148
|
+
continue
|
|
149
|
+
}
|
|
150
|
+
result.requires = initializer.elements.map((element) => (element as ts.StringLiteralLike).text)
|
|
151
|
+
}
|
|
71
152
|
|
|
72
153
|
return result
|
|
73
154
|
}
|
|
@@ -175,7 +256,6 @@ function collectBoundaryViolations(moduleDir: string): string[] {
|
|
|
175
256
|
|
|
176
257
|
function findResolvedPackageRoot(
|
|
177
258
|
resolvedPath: string,
|
|
178
|
-
packageName: string,
|
|
179
259
|
): string | null {
|
|
180
260
|
let currentPath = fs.statSync(resolvedPath).isDirectory()
|
|
181
261
|
? resolvedPath
|
|
@@ -185,7 +265,7 @@ function findResolvedPackageRoot(
|
|
|
185
265
|
const packageJsonPath = path.join(currentPath, 'package.json')
|
|
186
266
|
if (fs.existsSync(packageJsonPath)) {
|
|
187
267
|
const packageJson = readPackageJson(packageJsonPath)
|
|
188
|
-
if (packageJson.name ===
|
|
268
|
+
if (typeof packageJson.name === 'string' && normalizeModuleLocator(`npm:${packageJson.name}`) === `npm:${packageJson.name}`) {
|
|
189
269
|
return currentPath
|
|
190
270
|
}
|
|
191
271
|
}
|
|
@@ -195,7 +275,7 @@ function findResolvedPackageRoot(
|
|
|
195
275
|
return null
|
|
196
276
|
}
|
|
197
277
|
|
|
198
|
-
function resolveInstalledPackageRootWithRequire(packageName: string, appDir: string): string | null {
|
|
278
|
+
export function resolveInstalledPackageRootWithRequire(packageName: string, appDir: string): string | null {
|
|
199
279
|
const specifiers = [`${packageName}/package.json`, packageName]
|
|
200
280
|
|
|
201
281
|
for (const specifier of specifiers) {
|
|
@@ -205,7 +285,7 @@ function resolveInstalledPackageRootWithRequire(packageName: string, appDir: str
|
|
|
205
285
|
})
|
|
206
286
|
const packageRoot =
|
|
207
287
|
specifier === packageName
|
|
208
|
-
? findResolvedPackageRoot(resolvedPath
|
|
288
|
+
? findResolvedPackageRoot(resolvedPath)
|
|
209
289
|
: path.dirname(resolvedPath)
|
|
210
290
|
|
|
211
291
|
if (packageRoot) {
|
|
@@ -258,6 +338,130 @@ export function resolveInstalledPackageRoot(
|
|
|
258
338
|
throw new Error(`Package "${packageName}" is not installed in ${resolver.getAppDir()}.`)
|
|
259
339
|
}
|
|
260
340
|
|
|
341
|
+
function resolveEsmExportTarget(value: unknown, substitution?: string): string | null | undefined {
|
|
342
|
+
if (typeof value === 'string') {
|
|
343
|
+
const target = substitution === undefined ? value : value.replaceAll('*', substitution)
|
|
344
|
+
if (!target.startsWith('./') || target.slice(2).split('/').some((segment) => ['..', '.', 'node_modules'].includes(segment))) return undefined
|
|
345
|
+
return target
|
|
346
|
+
}
|
|
347
|
+
if (value === null) return null
|
|
348
|
+
if (Array.isArray(value)) {
|
|
349
|
+
for (const candidate of value) {
|
|
350
|
+
const target = resolveEsmExportTarget(candidate, substitution)
|
|
351
|
+
if (target !== undefined && target !== null) return target
|
|
352
|
+
}
|
|
353
|
+
return null
|
|
354
|
+
}
|
|
355
|
+
if (value && typeof value === 'object') {
|
|
356
|
+
for (const [condition, candidate] of Object.entries(value)) {
|
|
357
|
+
if (!['import', 'node', 'node-addons', 'module-sync', 'default'].includes(condition)) continue
|
|
358
|
+
const target = resolveEsmExportTarget(candidate, substitution)
|
|
359
|
+
if (target !== undefined) return target
|
|
360
|
+
}
|
|
361
|
+
}
|
|
362
|
+
return undefined
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
function resolvePackageSubpathExport(exports: unknown, subpath: string): string | null | undefined {
|
|
366
|
+
if (!exports || typeof exports !== 'object' || Array.isArray(exports)) return undefined
|
|
367
|
+
const entries = Object.entries(exports)
|
|
368
|
+
const direct = entries.find(([key]) => key === subpath)
|
|
369
|
+
if (direct) return resolveEsmExportTarget(direct[1])
|
|
370
|
+
const patterns = entries.filter(([key]) => key.startsWith('./') && key.includes('*'))
|
|
371
|
+
.sort(([left], [right]) => right.indexOf('*') - left.indexOf('*') || right.length - left.length)
|
|
372
|
+
for (const [pattern, target] of patterns) {
|
|
373
|
+
const wildcard = pattern.indexOf('*')
|
|
374
|
+
const prefix = pattern.slice(0, wildcard)
|
|
375
|
+
const suffix = pattern.slice(wildcard + 1)
|
|
376
|
+
if (subpath.startsWith(prefix) && subpath.endsWith(suffix) && subpath.length >= prefix.length + suffix.length) {
|
|
377
|
+
return resolveEsmExportTarget(target, subpath.slice(prefix.length, subpath.length - suffix.length))
|
|
378
|
+
}
|
|
379
|
+
}
|
|
380
|
+
return undefined
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
function isGeneratedModuleImport(relativePath: string, moduleFiles: readonly string[], readSource?: (relativePath: string) => string): boolean {
|
|
384
|
+
const segments = relativePath.split('/')
|
|
385
|
+
if (segments.some((segment) => segment === '__tests__' || segment === '__mocks__')) return false
|
|
386
|
+
const fileName = segments[segments.length - 1]
|
|
387
|
+
if (fileName.endsWith('.d.ts') || /\.(?:test|spec)\.[jt]sx?$/.test(fileName)) return false
|
|
388
|
+
const basename = stripModuleCodeExtension(fileName)
|
|
389
|
+
if (segments.length === 1) {
|
|
390
|
+
return new Set(['index', 'acl', 'setup', 'cli', 'runtime', 'ce', 'search', 'events', 'notifications', 'notifications.client', 'notifications.handlers', 'translations', 'encryption', 'ai-tools', 'ai-agents', 'di', 'integration', 'vector']).has(basename)
|
|
391
|
+
}
|
|
392
|
+
if (['widgets/components', 'widgets/injection-table', 'api/interceptors', 'data/extensions', 'data/fields'].includes(stripModuleCodeExtension(relativePath))) return true
|
|
393
|
+
if (/^commands\//.test(relativePath)) {
|
|
394
|
+
return MODULE_CODE_EXTENSIONS.some((extension) => fileName.endsWith(extension)) && !['index', 'shared', 'factory'].includes(basename)
|
|
395
|
+
}
|
|
396
|
+
const scanConfigs: readonly ScanConfig[] = Object.values(SCAN_CONFIGS)
|
|
397
|
+
for (const config of scanConfigs) {
|
|
398
|
+
if (!relativePath.startsWith(`${config.folder}/`)) continue
|
|
399
|
+
const relativeSegments = relativePath.slice(config.folder.length + 1).split('/')
|
|
400
|
+
if (relativeSegments.slice(0, -1).some((segment) => config.skipDirs?.(segment))) continue
|
|
401
|
+
if (!config.include(fileName)) continue
|
|
402
|
+
if (config.folder === 'frontend' || config.folder === 'backend') return readSource ? sourceHasDefaultExport(relativePath, readSource(relativePath)) : false
|
|
403
|
+
if (config.folder === 'api') return readSource ? sourceExportedHttpMethods(readSource(relativePath)).length > 0 : false
|
|
404
|
+
return true
|
|
405
|
+
}
|
|
406
|
+
if (/^(?:frontend|backend)\//.test(relativePath) && (basename === 'meta' || basename.endsWith('.meta'))) {
|
|
407
|
+
const folder = segments.slice(0, -1).join('/')
|
|
408
|
+
const pageConfig = segments[0] === 'frontend' ? SCAN_CONFIGS.frontendPages : SCAN_CONFIGS.backendPages
|
|
409
|
+
const candidates = moduleFiles.filter((file) => file.startsWith(`${folder}/`) && !file.slice(folder.length + 1).includes('/') && pageConfig.include(path.posix.basename(file)))
|
|
410
|
+
return candidates.some((candidate) => (basename === 'meta' || basename === `${stripModuleCodeExtension(path.posix.basename(candidate))}.meta`) && Boolean(readSource && sourceHasDefaultExport(candidate, readSource(candidate))))
|
|
411
|
+
}
|
|
412
|
+
if (/^(?:data|db)\/(?:entities\.override|entities|schema)\.[jt]sx?$/.test(relativePath)) {
|
|
413
|
+
const selected = ['data', 'db'].flatMap((folder) => ['entities.override', 'entities', 'schema'].flatMap((candidate) => MODULE_CODE_EXTENSIONS.map((extension) => `${folder}/${candidate}${extension}`)))
|
|
414
|
+
.find((candidate) => moduleFiles.includes(candidate))
|
|
415
|
+
return relativePath === selected
|
|
416
|
+
}
|
|
417
|
+
return /^i18n\/[^/]+\.json$/.test(relativePath)
|
|
418
|
+
}
|
|
419
|
+
|
|
420
|
+
export function isModulePackageExportError(error: unknown): boolean {
|
|
421
|
+
return error instanceof Error && 'code' in error && error.code === 'MODULE_PACKAGE_EXPORT_UNAVAILABLE'
|
|
422
|
+
}
|
|
423
|
+
|
|
424
|
+
export function assertModulePackageExports(
|
|
425
|
+
manifest: { exports?: unknown },
|
|
426
|
+
binding: string,
|
|
427
|
+
moduleId: string,
|
|
428
|
+
packageFiles: readonly string[],
|
|
429
|
+
readSource?: (relativePath: string) => string,
|
|
430
|
+
): void {
|
|
431
|
+
const normalizedFiles = new Set(packageFiles.map((file) => file.replaceAll('\\', '/').replace(/^\.\//, '')))
|
|
432
|
+
const sourcePrefix = `src/modules/${moduleId}/`
|
|
433
|
+
const distPrefix = `dist/modules/${moduleId}/`
|
|
434
|
+
const sourceFiles = [...normalizedFiles].filter((file) => file.startsWith(sourcePrefix)).map((file) => file.slice(sourcePrefix.length))
|
|
435
|
+
const distFiles = [...normalizedFiles].filter((file) => file.startsWith(distPrefix)).map((file) => file.slice(distPrefix.length))
|
|
436
|
+
const moduleFiles = sourceFiles.length > 0 ? sourceFiles : distFiles
|
|
437
|
+
const cachedSources = new Map<string, string>()
|
|
438
|
+
const readModuleSource = readSource ? (relativePath: string): string => {
|
|
439
|
+
let source = cachedSources.get(relativePath)
|
|
440
|
+
if (source === undefined) {
|
|
441
|
+
source = readSource(`${sourceFiles.length > 0 ? sourcePrefix : distPrefix}${relativePath}`)
|
|
442
|
+
cachedSources.set(relativePath, source)
|
|
443
|
+
}
|
|
444
|
+
return source
|
|
445
|
+
} : undefined
|
|
446
|
+
const generatedCandidates = sourceFiles.length > 0 ? sourceFiles : distFiles
|
|
447
|
+
for (const sourcePath of generatedCandidates) {
|
|
448
|
+
if (!isGeneratedModuleImport(sourcePath, moduleFiles, readModuleSource)) continue
|
|
449
|
+
const isCode = /\.[jt]sx?$/.test(sourcePath)
|
|
450
|
+
const runtimePath = sourceFiles.length > 0 && isCode
|
|
451
|
+
? distFiles.find((file) => /\.jsx?$/.test(file) && stripModuleCodeExtension(file) === stripModuleCodeExtension(sourcePath))
|
|
452
|
+
: sourcePath
|
|
453
|
+
const publicPath = `./modules/${moduleId}/${stripModuleCodeExtension(sourcePath)}`
|
|
454
|
+
if (!runtimePath) {
|
|
455
|
+
throw Object.assign(new Error(`Package binding "${binding}" cannot import "${binding}/${publicPath.slice(2)}": the package artifact is missing the compiled runtime entrypoint. Build the package before enabling it.`), { code: 'MODULE_PACKAGE_EXPORT_UNAVAILABLE' })
|
|
456
|
+
}
|
|
457
|
+
if (manifest.exports === undefined) continue
|
|
458
|
+
const target = resolvePackageSubpathExport(manifest.exports, publicPath)
|
|
459
|
+
if (!target || !normalizedFiles.has(target.slice(2))) {
|
|
460
|
+
throw Object.assign(new Error(`Package binding "${binding}" cannot import "${binding}/${publicPath.slice(2)}": the package exports do not provide an available ESM target. Keep the current provider and use a package exposing this module subpath before enabling it.`), { code: 'MODULE_PACKAGE_EXPORT_UNAVAILABLE' })
|
|
461
|
+
}
|
|
462
|
+
}
|
|
463
|
+
}
|
|
464
|
+
|
|
261
465
|
export function readOfficialModulePackageFromRoot(
|
|
262
466
|
packageRoot: string,
|
|
263
467
|
expectedPackageName?: string,
|
|
@@ -274,6 +478,10 @@ export function readOfficialModulePackageFromRoot(
|
|
|
274
478
|
throw new Error(`Package manifest at ${packageJsonPath} is missing the "name" field.`)
|
|
275
479
|
}
|
|
276
480
|
|
|
481
|
+
if (typeof packageName !== 'string' || normalizeModuleLocator(`npm:${packageName}`) !== `npm:${packageName}`) {
|
|
482
|
+
throw new Error(`Package manifest at ${packageJsonPath} has an invalid "name" field.`)
|
|
483
|
+
}
|
|
484
|
+
|
|
277
485
|
if (expectedPackageName && packageName !== expectedPackageName) {
|
|
278
486
|
throw new Error(`Resolved package "${packageName}" does not match requested package "${expectedPackageName}".`)
|
|
279
487
|
}
|
|
@@ -317,26 +525,45 @@ export function readOfficialModulePackageFromRoot(
|
|
|
317
525
|
throw new Error(`Package "${packageName}" is missing dist/modules/${moduleId}.`)
|
|
318
526
|
}
|
|
319
527
|
|
|
320
|
-
const info = parseModuleInfo(path.join(sourceModuleDir, 'index.ts'))
|
|
528
|
+
const info = parseModuleInfo(path.join(sourceModuleDir, 'index.ts'), true)
|
|
321
529
|
|
|
322
530
|
return {
|
|
323
531
|
packageName,
|
|
324
532
|
packageRoot,
|
|
325
533
|
packageJson,
|
|
326
534
|
metadata: { moduleId, ejectable: selected.ejectable },
|
|
327
|
-
moduleInfo: { title: info.title, description: info.description },
|
|
535
|
+
moduleInfo: { title: info.title, description: info.description, requires: info.requires, ...(info.locator ? { locator: info.locator } : {}) },
|
|
328
536
|
sourceModuleDir,
|
|
329
537
|
distModuleDir,
|
|
330
538
|
}
|
|
331
539
|
}
|
|
332
540
|
|
|
541
|
+
function listPackageFiles(packageRoot: string, directory = packageRoot): string[] {
|
|
542
|
+
return fs.readdirSync(directory, { withFileTypes: true }).flatMap((entry) => {
|
|
543
|
+
if (shouldSkipEntryName(entry.name)) return []
|
|
544
|
+
const fullPath = path.join(directory, entry.name)
|
|
545
|
+
return entry.isDirectory() ? listPackageFiles(packageRoot, fullPath) : [path.relative(packageRoot, fullPath)]
|
|
546
|
+
})
|
|
547
|
+
}
|
|
548
|
+
|
|
333
549
|
export function resolveInstalledOfficialModulePackage(
|
|
334
550
|
resolver: PackageResolver,
|
|
335
551
|
packageName: string,
|
|
336
552
|
moduleId?: string,
|
|
337
553
|
): ValidatedOfficialModulePackage {
|
|
338
554
|
const packageRoot = resolveInstalledPackageRoot(resolver, packageName)
|
|
339
|
-
|
|
555
|
+
const modulePackage = readOfficialModulePackageFromRoot(packageRoot, undefined, moduleId)
|
|
556
|
+
const packageFiles = listPackageFiles(packageRoot)
|
|
557
|
+
assertModulePackageExports(modulePackage.packageJson, packageName, modulePackage.metadata.moduleId, packageFiles, (relativePath) => fs.readFileSync(path.join(packageRoot, relativePath), 'utf8'))
|
|
558
|
+
if (modulePackage.packageName !== packageName) {
|
|
559
|
+
const resolvedBindingRoot = resolveInstalledPackageRootWithRequire(packageName, resolver.getAppDir())
|
|
560
|
+
const installedBindingPath = path.join('node_modules', packageName)
|
|
561
|
+
const matchesBindingPath = path.normalize(packageRoot).endsWith(`${path.sep}${installedBindingPath}`)
|
|
562
|
+
if (!matchesBindingPath && (!resolvedBindingRoot || fs.realpathSync(resolvedBindingRoot) !== fs.realpathSync(packageRoot))) {
|
|
563
|
+
throw new Error(`Resolved package "${modulePackage.packageName}" does not match requested package binding "${packageName}".`)
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
return modulePackage
|
|
340
567
|
}
|
|
341
568
|
|
|
342
569
|
export function validateEjectBoundaries(modulePackage: ValidatedOfficialModulePackage): void {
|
|
@@ -5,6 +5,8 @@ import { parseBooleanWithDefault } from '@open-mercato/shared/lib/boolean'
|
|
|
5
5
|
export type ModuleEntry = {
|
|
6
6
|
id: string
|
|
7
7
|
from?: '@open-mercato/core' | '@app' | string
|
|
8
|
+
locator?: string
|
|
9
|
+
packageName?: string
|
|
8
10
|
}
|
|
9
11
|
|
|
10
12
|
type ModuleOccurrence = {
|
|
@@ -24,14 +26,24 @@ type ModuleConfigShape = {
|
|
|
24
26
|
function parseModuleEntryFromObjectLiteral(node: ts.ObjectLiteralExpression): ModuleEntry | null {
|
|
25
27
|
let id: string | null = null
|
|
26
28
|
let from: string | null = null
|
|
29
|
+
let locator: string | undefined
|
|
30
|
+
let packageName: string | undefined
|
|
27
31
|
|
|
28
32
|
for (const property of node.properties) {
|
|
29
|
-
if (!ts.isPropertyAssignment(property) || !ts.isIdentifier(property.name)) continue
|
|
33
|
+
if (!ts.isPropertyAssignment(property) || (!ts.isIdentifier(property.name) && !ts.isStringLiteralLike(property.name))) continue
|
|
30
34
|
|
|
31
35
|
if (property.name.text === 'id' && ts.isStringLiteralLike(property.initializer)) {
|
|
32
36
|
id = property.initializer.text
|
|
33
37
|
}
|
|
34
38
|
|
|
39
|
+
if (property.name.text === 'packageName' && ts.isStringLiteralLike(property.initializer)) {
|
|
40
|
+
packageName = property.initializer.text
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
if (property.name.text === 'locator' && ts.isStringLiteralLike(property.initializer)) {
|
|
44
|
+
locator = property.initializer.text
|
|
45
|
+
}
|
|
46
|
+
|
|
35
47
|
if (property.name.text === 'from' && ts.isStringLiteralLike(property.initializer)) {
|
|
36
48
|
from = property.initializer.text
|
|
37
49
|
}
|
|
@@ -42,6 +54,8 @@ function parseModuleEntryFromObjectLiteral(node: ts.ObjectLiteralExpression): Mo
|
|
|
42
54
|
return {
|
|
43
55
|
id,
|
|
44
56
|
from: from ?? '@open-mercato/core',
|
|
57
|
+
...(locator ? { locator } : {}),
|
|
58
|
+
...(packageName ? { packageName } : {}),
|
|
45
59
|
}
|
|
46
60
|
}
|
|
47
61
|
|
|
@@ -368,6 +382,8 @@ function normalizeModuleEntry(entry: ModuleEntry): ModuleEntry {
|
|
|
368
382
|
return {
|
|
369
383
|
id: entry.id,
|
|
370
384
|
from: entry.from ?? '@open-mercato/core',
|
|
385
|
+
...(entry.locator ? { locator: entry.locator } : {}),
|
|
386
|
+
...(entry.packageName ? { packageName: entry.packageName } : {}),
|
|
371
387
|
}
|
|
372
388
|
}
|
|
373
389
|
|
|
@@ -377,7 +393,7 @@ function escapeSingleQuotedString(value: string): string {
|
|
|
377
393
|
|
|
378
394
|
function buildModuleEntryLiteral(entry: ModuleEntry): string {
|
|
379
395
|
const normalized = normalizeModuleEntry(entry)
|
|
380
|
-
return `{ id: '${escapeSingleQuotedString(normalized.id)}', from: '${escapeSingleQuotedString(normalized.from ?? '@open-mercato/core')}' }`
|
|
396
|
+
return `{ id: '${escapeSingleQuotedString(normalized.id)}', from: '${escapeSingleQuotedString(normalized.from ?? '@open-mercato/core')}'${normalized.locator ? `, locator: '${escapeSingleQuotedString(normalized.locator)}'` : ''}${normalized.packageName ? `, packageName: '${escapeSingleQuotedString(normalized.packageName)}'` : ''} }`
|
|
381
397
|
}
|
|
382
398
|
|
|
383
399
|
function lineIndentAt(source: string, position: number): string {
|
|
@@ -443,16 +459,29 @@ function insertArrayElement(
|
|
|
443
459
|
return head + insertion + closingLine + tail
|
|
444
460
|
}
|
|
445
461
|
|
|
446
|
-
function
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
if (
|
|
452
|
-
|
|
453
|
-
}
|
|
454
|
-
|
|
455
|
-
|
|
462
|
+
function upsertModuleProperty(objectLiteral: string, propertyName: 'from' | 'locator' | 'packageName', value: string): string {
|
|
463
|
+
const prefix = 'const entry = '
|
|
464
|
+
const sourceFile = ts.createSourceFile('entry.ts', prefix + objectLiteral, ts.ScriptTarget.Latest, true)
|
|
465
|
+
const statement = sourceFile.statements[0]
|
|
466
|
+
const initializer = ts.isVariableStatement(statement) ? statement.declarationList.declarations[0]?.initializer : undefined
|
|
467
|
+
if (!initializer || !ts.isObjectLiteralExpression(initializer)) {
|
|
468
|
+
throw new Error('Cannot update module registration: expected an object literal.')
|
|
469
|
+
}
|
|
470
|
+
const property = initializer.properties.find((candidate) =>
|
|
471
|
+
'name' in candidate && candidate.name && (ts.isIdentifier(candidate.name) || ts.isStringLiteralLike(candidate.name)) && candidate.name.text === propertyName,
|
|
472
|
+
)
|
|
473
|
+
const quotedValue = `'${escapeSingleQuotedString(value)}'`
|
|
474
|
+
if (property) {
|
|
475
|
+
const target = ts.isPropertyAssignment(property) ? property.initializer : property
|
|
476
|
+
const existingQuote = ts.isPropertyAssignment(property) ? property.initializer.getText(sourceFile)[0] : undefined
|
|
477
|
+
const replacementValue = existingQuote === '"' ? JSON.stringify(value) : quotedValue
|
|
478
|
+
const replacement = ts.isPropertyAssignment(property) ? replacementValue : `${propertyName}: ${replacementValue}`
|
|
479
|
+
return objectLiteral.slice(0, target.getStart(sourceFile) - prefix.length) + replacement + objectLiteral.slice(target.end - prefix.length)
|
|
480
|
+
}
|
|
481
|
+
const lastProperty = initializer.properties[initializer.properties.length - 1]
|
|
482
|
+
const insertPosition = lastProperty ? lastProperty.end - prefix.length : initializer.end - prefix.length - 1
|
|
483
|
+
const separator = lastProperty ? ', ' : ' '
|
|
484
|
+
return objectLiteral.slice(0, insertPosition) + `${separator}${propertyName}: ${quotedValue}` + objectLiteral.slice(insertPosition)
|
|
456
485
|
}
|
|
457
486
|
|
|
458
487
|
export function readEnabledModules(modulesPath: string, env: NodeJS.ProcessEnv = process.env): ModuleEntry[] {
|
|
@@ -515,6 +544,16 @@ export function ensureModuleRegistration(
|
|
|
515
544
|
}
|
|
516
545
|
|
|
517
546
|
const source = fs.readFileSync(modulesPath, 'utf8')
|
|
547
|
+
const result = prepareModuleRegistration(source, entry, modulesPath)
|
|
548
|
+
if (result.changed) fs.writeFileSync(modulesPath, result.source)
|
|
549
|
+
return { changed: result.changed, registeredAs: result.registeredAs }
|
|
550
|
+
}
|
|
551
|
+
|
|
552
|
+
export function prepareModuleRegistration(
|
|
553
|
+
source: string,
|
|
554
|
+
entry: ModuleEntry,
|
|
555
|
+
modulesPath = 'src/modules.ts',
|
|
556
|
+
): { source: string; changed: boolean; registeredAs: string } {
|
|
518
557
|
const sourceFile = ts.createSourceFile('modules.ts', source, ts.ScriptTarget.Latest, true, ts.ScriptKind.TS)
|
|
519
558
|
const config = parseModuleConfigShape(source)
|
|
520
559
|
const normalized = normalizeModuleEntry(entry)
|
|
@@ -526,67 +565,76 @@ export function ensureModuleRegistration(
|
|
|
526
565
|
}
|
|
527
566
|
|
|
528
567
|
if (matches.length > 1) {
|
|
529
|
-
throw new Error(
|
|
530
|
-
`Module "${normalized.id}" is registered multiple times in ${modulesPath}. Resolve duplicates manually before running this command.`,
|
|
531
|
-
)
|
|
568
|
+
throw new Error(`Module "${normalized.id}" is registered multiple times in src/modules.ts. Resolve duplicates manually before running this command.`)
|
|
532
569
|
}
|
|
533
570
|
|
|
534
571
|
if (matches.length === 1) {
|
|
535
572
|
const existing = normalizeModuleEntry(matches[0].entry)
|
|
536
573
|
if (existing.from === normalized.from) {
|
|
537
|
-
return { changed: false, registeredAs: normalized.from ?? '@open-mercato/core' }
|
|
574
|
+
return { source, changed: false, registeredAs: normalized.from ?? '@open-mercato/core' }
|
|
538
575
|
}
|
|
539
|
-
|
|
540
|
-
throw new Error(
|
|
541
|
-
`Module "${normalized.id}" is already registered from "${existing.from}". Remove or eject the existing registration first.`,
|
|
542
|
-
)
|
|
576
|
+
throw new Error(`Module "${normalized.id}" is already registered from "${existing.from}". Remove or eject the existing registration first.`)
|
|
543
577
|
}
|
|
544
578
|
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
579
|
+
return {
|
|
580
|
+
source: insertArrayElement(source, config.arrayNode, normalized, sourceFile),
|
|
581
|
+
changed: true,
|
|
582
|
+
registeredAs: normalized.from ?? '@open-mercato/core',
|
|
583
|
+
}
|
|
549
584
|
}
|
|
550
585
|
|
|
551
586
|
export function setModuleRegistrationSource(
|
|
552
587
|
modulesPath: string,
|
|
553
588
|
moduleId: string,
|
|
554
589
|
from: string,
|
|
590
|
+
provenance?: Pick<ModuleEntry, 'locator' | 'packageName'>,
|
|
555
591
|
): { changed: boolean } {
|
|
556
592
|
if (!fs.existsSync(modulesPath)) {
|
|
557
593
|
throw new Error(`modules.ts not found at ${modulesPath}`)
|
|
558
594
|
}
|
|
559
595
|
|
|
560
596
|
const source = fs.readFileSync(modulesPath, 'utf8')
|
|
597
|
+
const updated = prepareModuleRegistrationSource(source, moduleId, from, provenance)
|
|
598
|
+
if (updated === source) return { changed: false }
|
|
599
|
+
fs.writeFileSync(modulesPath, updated)
|
|
600
|
+
return { changed: true }
|
|
601
|
+
}
|
|
602
|
+
|
|
603
|
+
export function prepareModuleRegistrationSource(
|
|
604
|
+
source: string,
|
|
605
|
+
moduleId: string,
|
|
606
|
+
from: string,
|
|
607
|
+
provenance?: Pick<ModuleEntry, 'locator' | 'packageName'>,
|
|
608
|
+
): string {
|
|
561
609
|
const config = parseModuleConfigShape(source)
|
|
562
610
|
const matches = config.occurrences.filter((occurrence) => occurrence.active && occurrence.entry.id === moduleId)
|
|
563
611
|
|
|
564
612
|
if (matches.length === 0) {
|
|
565
613
|
throw new Error(
|
|
566
|
-
`Could not find module entry for "${moduleId}" in
|
|
614
|
+
`Could not find module entry for "${moduleId}" in src/modules.ts. Expected a pattern like: { id: '${moduleId}', from: '...' } or { id: '${moduleId}' }`,
|
|
567
615
|
)
|
|
568
616
|
}
|
|
569
617
|
|
|
570
618
|
if (matches.length > 1) {
|
|
571
619
|
throw new Error(
|
|
572
|
-
`Module "${moduleId}" is registered multiple times in
|
|
620
|
+
`Module "${moduleId}" is registered multiple times in src/modules.ts. Resolve duplicates manually before running this command.`,
|
|
573
621
|
)
|
|
574
622
|
}
|
|
575
623
|
|
|
576
624
|
const match = matches[0]
|
|
577
625
|
const current = normalizeModuleEntry(match.entry)
|
|
578
|
-
if (current.from === from) {
|
|
579
|
-
return
|
|
626
|
+
if (current.from === from && (!provenance?.locator || current.locator === provenance.locator) && (!provenance?.packageName || current.packageName === provenance.packageName)) {
|
|
627
|
+
return source
|
|
580
628
|
}
|
|
581
629
|
|
|
582
630
|
const objectLiteral = source.slice(match.node.getStart(), match.node.end)
|
|
583
|
-
|
|
631
|
+
let updatedObjectLiteral = upsertModuleProperty(objectLiteral, 'from', from)
|
|
632
|
+
if (provenance?.locator) updatedObjectLiteral = upsertModuleProperty(updatedObjectLiteral, 'locator', provenance.locator)
|
|
633
|
+
if (provenance?.packageName) updatedObjectLiteral = upsertModuleProperty(updatedObjectLiteral, 'packageName', provenance.packageName)
|
|
584
634
|
const updated =
|
|
585
635
|
source.slice(0, match.node.getStart()) +
|
|
586
636
|
updatedObjectLiteral +
|
|
587
637
|
source.slice(match.node.end)
|
|
588
638
|
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
return { changed: true }
|
|
639
|
+
return updated
|
|
592
640
|
}
|