@salesforce/storefront-next-dev 1.1.0-alpha.3 → 1.2.0-alpha.0
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/README.md +1 -1
- package/dist/cartridge-services/index.d.ts +1 -1
- package/dist/cartridge-services/index.d.ts.map +1 -1
- package/dist/cartridge-services/index.js +134 -23
- package/dist/cartridge-services/index.js.map +1 -1
- package/dist/commands/create-storefront.js +20 -1
- package/dist/commands/extensions/create.js +1 -0
- package/dist/commands/extensions/install.js +1 -0
- package/dist/commands/extensions/list.js +1 -0
- package/dist/commands/extensions/remove.js +1 -0
- package/dist/commands/generate-cartridge.js +1 -0
- package/dist/commands/push.js +1 -0
- package/dist/commands/scapi/available.js +6 -0
- package/dist/configs/react-router.config.d.ts +5 -1
- package/dist/configs/react-router.config.d.ts.map +1 -1
- package/dist/configs/react-router.config.js +6 -2
- package/dist/configs/react-router.config.js.map +1 -1
- package/dist/dependency-utils.js +4 -54
- package/dist/entry/server.js +49 -13
- package/dist/entry/server.js.map +1 -1
- package/dist/format-with-project-biome.js +108 -0
- package/dist/generate-cartridge.js +78 -23
- package/dist/generate-custom-clients.js +1 -0
- package/dist/index.js +75 -41
- package/dist/index.js.map +1 -1
- package/dist/logger/index.js.map +1 -1
- package/dist/logger.js +1 -1
- package/dist/mrt/ssr.mjs +6 -8
- package/dist/mrt/ssr.mjs.map +1 -1
- package/dist/mrt/streamingHandler.mjs +22 -24
- package/dist/mrt/streamingHandler.mjs.map +1 -1
- package/dist/react-router/Scripts.js.map +1 -1
- package/dist/server.js +2 -2
- package/package.json +10 -8
|
@@ -1,9 +1,10 @@
|
|
|
1
1
|
import { t as logger } from "./logger.js";
|
|
2
|
+
import { t as formatDirectoryWithProjectBiome } from "./format-with-project-biome.js";
|
|
2
3
|
import { existsSync, readFileSync, unlinkSync } from "node:fs";
|
|
3
4
|
import { basename, extname, join, resolve } from "node:path";
|
|
4
5
|
import { access, mkdir, readFile, readdir, rm, writeFile } from "node:fs/promises";
|
|
5
|
-
import { execSync } from "node:child_process";
|
|
6
6
|
import { Node, Project } from "ts-morph";
|
|
7
|
+
import { execSync } from "node:child_process";
|
|
7
8
|
import { tmpdir } from "node:os";
|
|
8
9
|
import { randomUUID } from "node:crypto";
|
|
9
10
|
import { npmRunPathEnv } from "npm-run-path";
|
|
@@ -159,6 +160,62 @@ function resolveAttributeType(decoratorType, tsMorphType, fieldName) {
|
|
|
159
160
|
if (tsMorphType && TYPE_MAPPING[tsMorphType]) return TYPE_MAPPING[tsMorphType];
|
|
160
161
|
return "string";
|
|
161
162
|
}
|
|
163
|
+
const SEARCHING_FORBIDDEN_TYPES = new Set([
|
|
164
|
+
"integer",
|
|
165
|
+
"boolean",
|
|
166
|
+
"file",
|
|
167
|
+
"page",
|
|
168
|
+
"image",
|
|
169
|
+
"url",
|
|
170
|
+
"enum"
|
|
171
|
+
]);
|
|
172
|
+
/**
|
|
173
|
+
* Validate an authored `searching` config against the resolved attribute type and map it to the
|
|
174
|
+
* snake_case schema shape. The metadefinition schema gates `searching` by type; an invalid
|
|
175
|
+
* combination fails cartridge generation (matching the strict behavior of `resolveAttributeType`).
|
|
176
|
+
*
|
|
177
|
+
* Rules (from attributedefinition.json):
|
|
178
|
+
* - Forbidden entirely on integer/boolean/file/page/image/url/enum.
|
|
179
|
+
* - `markup`: `sortable` must be false.
|
|
180
|
+
* - `custom`/`cms_record`: `refinable` must be false; `boost_factor`/`sortable` not allowed.
|
|
181
|
+
* - `string`/`text`/`product`/`category`: all fields allowed.
|
|
182
|
+
*/
|
|
183
|
+
function mapAttributeSearching(searching, resolvedType, fieldName) {
|
|
184
|
+
const fail = (reason) => {
|
|
185
|
+
logger.error(`Invalid 'searching' config for attribute '${fieldName}' (type '${resolvedType}'): ${reason}`);
|
|
186
|
+
process.exit(1);
|
|
187
|
+
};
|
|
188
|
+
if (SEARCHING_FORBIDDEN_TYPES.has(resolvedType)) fail(`the '${resolvedType}' attribute type does not support searching.`);
|
|
189
|
+
const { searchable, refinable, boostFactor, sortable } = searching;
|
|
190
|
+
if (typeof searchable !== "boolean" || typeof refinable !== "boolean") fail(`'searchable' and 'refinable' are required booleans.`);
|
|
191
|
+
if (resolvedType === "markup" && sortable === true) fail(`'sortable' must be false for markup attributes.`);
|
|
192
|
+
if (resolvedType === "custom" || resolvedType === "cms_record") {
|
|
193
|
+
if (refinable === true) fail(`'refinable' must be false for ${resolvedType} attributes.`);
|
|
194
|
+
if (boostFactor !== void 0) fail(`'boostFactor' is not supported for ${resolvedType} attributes.`);
|
|
195
|
+
if (sortable === true) fail(`'sortable' must be false for ${resolvedType} attributes.`);
|
|
196
|
+
}
|
|
197
|
+
const mapped = {
|
|
198
|
+
searchable,
|
|
199
|
+
refinable
|
|
200
|
+
};
|
|
201
|
+
if (boostFactor !== void 0) mapped.boost_factor = boostFactor;
|
|
202
|
+
if (sortable !== void 0) mapped.sortable = sortable;
|
|
203
|
+
return mapped;
|
|
204
|
+
}
|
|
205
|
+
/**
|
|
206
|
+
* Validate an authored `dynamicLookup` config and map it to the snake_case schema shape.
|
|
207
|
+
* The metadefinition schema allows `dynamic_lookup` on all attribute types but requires a
|
|
208
|
+
* single `aspect_attribute_alias` field (`additionalProperties: false`); a missing alias fails
|
|
209
|
+
* cartridge generation (matching the strict behavior of `resolveAttributeType`).
|
|
210
|
+
*/
|
|
211
|
+
function mapAttributeDynamicLookup(dynamicLookup, fieldName) {
|
|
212
|
+
const { aspectAttributeAlias } = dynamicLookup;
|
|
213
|
+
if (typeof aspectAttributeAlias !== "string" || aspectAttributeAlias.length === 0) {
|
|
214
|
+
logger.error(`Invalid 'dynamicLookup' config for attribute '${fieldName}': 'aspectAttributeAlias' is required and must be a non-empty string.`);
|
|
215
|
+
process.exit(1);
|
|
216
|
+
}
|
|
217
|
+
return { aspect_attribute_alias: aspectAttributeAlias };
|
|
218
|
+
}
|
|
162
219
|
function toHumanReadableName(fieldName) {
|
|
163
220
|
return fieldName.replace(/([A-Z])/g, " $1").replace(/^./, (str) => str.toUpperCase()).trim();
|
|
164
221
|
}
|
|
@@ -310,6 +367,8 @@ function extractAttributesFromSource(sourceFile, className) {
|
|
|
310
367
|
if (config.values) attribute.values = config.values;
|
|
311
368
|
if (config.defaultValue !== void 0) attribute.default_value = config.defaultValue;
|
|
312
369
|
if (config.editorDefinition !== void 0) attribute.editor_definition = config.editorDefinition;
|
|
370
|
+
if (config.searching !== void 0) attribute.searching = mapAttributeSearching(config.searching, attribute.type, String(attribute.id));
|
|
371
|
+
if (config.dynamicLookup !== void 0) attribute.dynamic_lookup = mapAttributeDynamicLookup(config.dynamicLookup, String(attribute.id));
|
|
313
372
|
attributes.push(attribute);
|
|
314
373
|
}
|
|
315
374
|
} catch (error) {
|
|
@@ -339,13 +398,19 @@ function extractRegionDefinitionsFromSource(sourceFile, className, defaultCompon
|
|
|
339
398
|
id: regionConfig.id || "region",
|
|
340
399
|
name: regionConfig.name || "Region"
|
|
341
400
|
};
|
|
401
|
+
if (regionConfig.description !== void 0) regionDefinition.description = regionConfig.description;
|
|
342
402
|
if (regionConfig.componentTypes) regionDefinition.component_types = regionConfig.componentTypes;
|
|
343
403
|
if (Array.isArray(regionConfig.componentTypeInclusions)) regionDefinition.component_type_inclusions = regionConfig.componentTypeInclusions.map((incl) => ({ type_id: normalizeComponentTypeId(String(incl), defaultComponentGroup) }));
|
|
344
404
|
if (Array.isArray(regionConfig.componentTypeExclusions)) regionDefinition.component_type_exclusions = regionConfig.componentTypeExclusions.map((excl) => ({ type_id: normalizeComponentTypeId(String(excl), defaultComponentGroup) }));
|
|
345
405
|
if (regionConfig.maxComponents !== void 0) regionDefinition.max_components = regionConfig.maxComponents;
|
|
346
406
|
if (regionConfig.minComponents !== void 0) regionDefinition.min_components = regionConfig.minComponents;
|
|
347
407
|
if (regionConfig.allowMultiple !== void 0) regionDefinition.allow_multiple = regionConfig.allowMultiple;
|
|
348
|
-
if (regionConfig.defaultComponentConstructors) regionDefinition.default_component_constructors = regionConfig.defaultComponentConstructors
|
|
408
|
+
if (Array.isArray(regionConfig.defaultComponentConstructors)) regionDefinition.default_component_constructors = regionConfig.defaultComponentConstructors.map((constructor) => {
|
|
409
|
+
const { typeId, name } = constructor;
|
|
410
|
+
const mapped = { type_id: normalizeComponentTypeId(String(typeId), defaultComponentGroup) };
|
|
411
|
+
if (name !== void 0) mapped.name = name;
|
|
412
|
+
return mapped;
|
|
413
|
+
});
|
|
349
414
|
regionDefinitions.push(regionDefinition);
|
|
350
415
|
}
|
|
351
416
|
}
|
|
@@ -426,7 +491,8 @@ async function processPageTypeFile(filePath, projectRoot) {
|
|
|
426
491
|
regionDefinitions,
|
|
427
492
|
supportedAspectTypes: pageTypeConfig.supportedAspectTypes || [],
|
|
428
493
|
attributes,
|
|
429
|
-
route
|
|
494
|
+
route,
|
|
495
|
+
...pageTypeConfig.preview !== void 0 && { preview: pageTypeConfig.preview }
|
|
430
496
|
};
|
|
431
497
|
pageTypes.push(pageTypeMetadata);
|
|
432
498
|
}
|
|
@@ -513,6 +579,7 @@ async function generatePageTypeCartridge(pageType, outputDir, dryRun = false) {
|
|
|
513
579
|
}];
|
|
514
580
|
if (pageType.supportedAspectTypes) cartridgeData.supported_aspect_types = pageType.supportedAspectTypes;
|
|
515
581
|
if (pageType.route) cartridgeData.route = pageType.route;
|
|
582
|
+
if (pageType.preview !== void 0) cartridgeData.preview = pageType.preview;
|
|
516
583
|
await writeFile(outputPath, JSON.stringify(cartridgeData, null, 2));
|
|
517
584
|
}
|
|
518
585
|
const prefix = dryRun ? " - [DRY RUN]" : " -";
|
|
@@ -535,26 +602,14 @@ async function generateAspectCartridge(aspect, outputDir, dryRun = false) {
|
|
|
535
602
|
logger.debug(`${prefix} ${String(aspect.name)}: ${String(aspect.description)} (${String(aspect.attributeDefinitions.length)} attributes) → ${fileName}.json`);
|
|
536
603
|
}
|
|
537
604
|
/**
|
|
538
|
-
*
|
|
539
|
-
*
|
|
605
|
+
* Formats the generated JSON files in place so they match the project's `biome format` output
|
|
606
|
+
* (and therefore pass `pnpm lint` out of the box). Delegates to the shared Biome helper, which
|
|
607
|
+
* prefers the consuming project's Biome and is fail-safe (logs and leaves files valid-but-
|
|
608
|
+
* unformatted rather than throwing) when Biome can't run.
|
|
540
609
|
*/
|
|
541
|
-
function lintGeneratedFiles(metadataDir
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
execSync(`npx eslint "${metadataDir}/**/*.json" --fix --no-error-on-unmatched-pattern`, {
|
|
545
|
-
cwd: projectRoot,
|
|
546
|
-
stdio: "pipe",
|
|
547
|
-
encoding: "utf-8"
|
|
548
|
-
});
|
|
549
|
-
logger.debug("✅ JSON files formatted successfully");
|
|
550
|
-
} catch (error) {
|
|
551
|
-
const execError = error;
|
|
552
|
-
if (execError.status === 2) {
|
|
553
|
-
const errMsg = execError.stderr || execError.stdout || "Unknown error";
|
|
554
|
-
logger.warn(`⚠️ Could not run ESLint --fix: ${errMsg}`);
|
|
555
|
-
} else if (execError.stderr && execError.stderr.includes("error")) logger.warn(`⚠️ Some linting issues could not be auto-fixed. Run ESLint manually to review.`);
|
|
556
|
-
else logger.debug("✅ JSON files formatted successfully");
|
|
557
|
-
}
|
|
610
|
+
function lintGeneratedFiles(metadataDir) {
|
|
611
|
+
logger.debug("🔧 Formatting generated JSON files with Biome...");
|
|
612
|
+
formatDirectoryWithProjectBiome(metadataDir);
|
|
558
613
|
}
|
|
559
614
|
async function generateMetadata(projectDirectory, metadataDirectory, options) {
|
|
560
615
|
try {
|
|
@@ -669,7 +724,7 @@ async function generateMetadata(projectDirectory, metadataDirectory, options) {
|
|
|
669
724
|
else logger.info(`Generated ${allAspects.length} aspect metadata file(s)`);
|
|
670
725
|
}
|
|
671
726
|
const shouldLintFix = options?.lintFix !== false;
|
|
672
|
-
if (!dryRun && shouldLintFix && (allComponents.length > 0 || allPageTypes.length > 0 || allAspects.length > 0)) lintGeneratedFiles(metadataDir
|
|
727
|
+
if (!dryRun && shouldLintFix && (allComponents.length > 0 || allPageTypes.length > 0 || allAspects.length > 0)) lintGeneratedFiles(metadataDir);
|
|
673
728
|
return {
|
|
674
729
|
componentsGenerated: allComponents.length,
|
|
675
730
|
pageTypesGenerated: allPageTypes.length,
|
|
@@ -34,6 +34,7 @@ const BUILT_IN_NAMESPACE_BY_KEY = {
|
|
|
34
34
|
shopperConsents: "ShopperConsents",
|
|
35
35
|
shopperContext: "ShopperContext",
|
|
36
36
|
shopperCustomers: "ShopperCustomers",
|
|
37
|
+
shopperDeliveryEstimates: "ShopperDeliveryEstimates",
|
|
37
38
|
shopperExperience: "ShopperExperience",
|
|
38
39
|
shopperGiftCertificates: "ShopperGiftCertificates",
|
|
39
40
|
shopperLogin: "ShopperLogin",
|
package/dist/index.js
CHANGED
|
@@ -2,7 +2,7 @@ import path, { resolve } from "node:path";
|
|
|
2
2
|
import fs from "fs-extra";
|
|
3
3
|
import chalk from "chalk";
|
|
4
4
|
import { createRequire } from "module";
|
|
5
|
-
import path$1, { dirname, join, relative, resolve as resolve$1 } from "path";
|
|
5
|
+
import path$1, { basename, dirname, join, relative, resolve as resolve$1 } from "path";
|
|
6
6
|
import { fileURLToPath } from "url";
|
|
7
7
|
import { parse } from "@babel/parser";
|
|
8
8
|
import { isArrayPattern, isClassDeclaration, isExportSpecifier, isFunctionDeclaration, isIdentifier, isJSXAttribute, isJSXElement, isJSXFragment, isJSXIdentifier, isMemberExpression, isObjectPattern, isObjectProperty, isRestElement, isVariableDeclaration, jsxClosingElement, jsxClosingFragment, jsxElement, jsxFragment, jsxIdentifier, jsxOpeningElement, jsxOpeningFragment, jsxText } from "@babel/types";
|
|
@@ -11,6 +11,7 @@ import traverseModule from "@babel/traverse";
|
|
|
11
11
|
import { existsSync, readFileSync, writeFileSync } from "fs";
|
|
12
12
|
import { glob } from "glob";
|
|
13
13
|
import { Node, Project, ts } from "ts-morph";
|
|
14
|
+
import { spawnSync } from "child_process";
|
|
14
15
|
import fs$1, { existsSync as existsSync$1, readFileSync as readFileSync$1 } from "node:fs";
|
|
15
16
|
import { deadCodeElimination, findReferencedIdentifiers } from "babel-dead-code-elimination";
|
|
16
17
|
import httpProxy from "http-proxy";
|
|
@@ -805,6 +806,72 @@ const watchConfigFilesPlugin = () => {
|
|
|
805
806
|
};
|
|
806
807
|
};
|
|
807
808
|
|
|
809
|
+
//#endregion
|
|
810
|
+
//#region src/utils/format-with-project-biome.ts
|
|
811
|
+
/** This module's own path — the resolution root for the SDK-bundled Biome fallback. */
|
|
812
|
+
const HERE = fileURLToPath(import.meta.url);
|
|
813
|
+
/**
|
|
814
|
+
* Format generated file content with the *consuming project's* Biome so the written file
|
|
815
|
+
* matches what the project's own `biome format` / `pnpm lint:fix` would produce.
|
|
816
|
+
*
|
|
817
|
+
* The SDK generates two kinds of files into a customer project — the extension `config.json`
|
|
818
|
+
* (`trim-extensions.ts`) and the static component registry (`staticRegistry.ts`). Both must
|
|
819
|
+
* be emitted in the exact byte shape the project's formatter produces, or the generated file
|
|
820
|
+
* fails `pnpm lint` on a fresh project / churns on every `biome format --write` (W-23074938).
|
|
821
|
+
*
|
|
822
|
+
* Biome is preferred from the target file's own location — i.e. the consuming project's
|
|
823
|
+
* `node_modules` — so it's the project's Biome version whose output is matched. When that
|
|
824
|
+
* isn't installed yet (e.g. `create-storefront` runs `trim-extensions` BEFORE the generated
|
|
825
|
+
* project's first `pnpm install`), we fall back to the SDK-bundled `@biomejs/biome` (a hard
|
|
826
|
+
* dependency of this package, pinned to the template's version). Either way the file's
|
|
827
|
+
* committed `biome.json` governs the output: Biome discovers its config by walking up from
|
|
828
|
+
* the working directory, so we spawn it with `cwd` set to the file's directory and pass the
|
|
829
|
+
* file's basename via `--stdin-file-path` (which drives both parser selection and formatting).
|
|
830
|
+
*
|
|
831
|
+
* Returns the content unchanged when no Biome can be resolved at all or the format fails, so
|
|
832
|
+
* generation never breaks over formatting. A genuine format error is logged but non-fatal —
|
|
833
|
+
* an unformatted-but-valid file is recoverable by the customer running `biome format --write`.
|
|
834
|
+
*
|
|
835
|
+
* @param content - The serialized file content to format.
|
|
836
|
+
* @param filePath - The file's path (drives parser selection + config resolution + cwd).
|
|
837
|
+
* @returns The Biome-formatted content, or the original content if Biome is unavailable.
|
|
838
|
+
*/
|
|
839
|
+
function formatWithProjectBiome(content, filePath) {
|
|
840
|
+
const biomeBin = resolveBiomeBin(filePath);
|
|
841
|
+
if (!biomeBin) {
|
|
842
|
+
logger.warn(`⚠️ Biome could not be resolved; ${basename(filePath)} will be written unformatted.`);
|
|
843
|
+
return content;
|
|
844
|
+
}
|
|
845
|
+
const result = spawnSync(process.execPath, [
|
|
846
|
+
biomeBin,
|
|
847
|
+
"format",
|
|
848
|
+
`--stdin-file-path=${basename(filePath)}`
|
|
849
|
+
], {
|
|
850
|
+
cwd: dirname(filePath),
|
|
851
|
+
input: content,
|
|
852
|
+
encoding: "utf8"
|
|
853
|
+
});
|
|
854
|
+
if (result.status !== 0 || typeof result.stdout !== "string") {
|
|
855
|
+
const detail = result.stderr?.trim() || `exit code ${result.status}`;
|
|
856
|
+
logger.warn(`⚠️ Skipping Biome formatting for ${basename(filePath)}: ${detail}`);
|
|
857
|
+
return content;
|
|
858
|
+
}
|
|
859
|
+
return result.stdout;
|
|
860
|
+
}
|
|
861
|
+
/**
|
|
862
|
+
* Resolve a Biome CLI binary path, preferring the consuming project's install and falling back
|
|
863
|
+
* to the SDK-bundled copy (available pre-install). Returns null when neither resolves.
|
|
864
|
+
*/
|
|
865
|
+
function resolveBiomeBin(filePath) {
|
|
866
|
+
for (const fromPath of [filePath, HERE]) try {
|
|
867
|
+
const req = createRequire(fromPath);
|
|
868
|
+
const biomePkgJson = req.resolve("@biomejs/biome/package.json");
|
|
869
|
+
const { bin } = req(biomePkgJson);
|
|
870
|
+
return join(dirname(biomePkgJson), bin.biome);
|
|
871
|
+
} catch {}
|
|
872
|
+
return null;
|
|
873
|
+
}
|
|
874
|
+
|
|
808
875
|
//#endregion
|
|
809
876
|
//#region src/plugins/staticRegistry.ts
|
|
810
877
|
const DEFAULT_COMPONENT_GROUP = "storefrontnext_base";
|
|
@@ -958,42 +1025,9 @@ ${registrations}
|
|
|
958
1025
|
`;
|
|
959
1026
|
}
|
|
960
1027
|
/**
|
|
961
|
-
* Formats registry file content with the project's own Prettier so the written file
|
|
962
|
-
* matches what the project's formatter would produce.
|
|
963
|
-
*
|
|
964
|
-
* Without this, a standalone `prettier --write` (pre-commit hook, format-on-save) rewrites
|
|
965
|
-
* the generated file on every commit: the generator emits one `registerImporter` call per
|
|
966
|
-
* line and cannot anticipate an arbitrary `printWidth`, so Prettier re-wraps long lines and
|
|
967
|
-
* the regenerate -> format -> regenerate loop never settles.
|
|
968
|
-
*
|
|
969
|
-
* Prettier is resolved from the registry file's own location — i.e. the consuming project's
|
|
970
|
-
* `node_modules`, not the SDK bundle (which keeps `node_modules` external). It is the project's
|
|
971
|
-
* Prettier version and config whose output must be matched. Returns the content unchanged when
|
|
972
|
-
* Prettier is absent or fails, so registry generation never breaks a build over formatting.
|
|
973
|
-
*/
|
|
974
|
-
let warnedNoPrettier = false;
|
|
975
|
-
async function formatWithProjectPrettier(content, registryFilePath) {
|
|
976
|
-
try {
|
|
977
|
-
const prettier = createRequire(registryFilePath)("prettier");
|
|
978
|
-
const config = await prettier.resolveConfig(registryFilePath, { editorconfig: true });
|
|
979
|
-
return await prettier.format(content, {
|
|
980
|
-
...config,
|
|
981
|
-
filepath: registryFilePath
|
|
982
|
-
});
|
|
983
|
-
} catch (error) {
|
|
984
|
-
if (error.code === "MODULE_NOT_FOUND") {
|
|
985
|
-
if (!warnedNoPrettier) {
|
|
986
|
-
logger.warn("⚠️ Prettier not found in the project; static registry will be written unformatted.");
|
|
987
|
-
warnedNoPrettier = true;
|
|
988
|
-
}
|
|
989
|
-
} else logger.warn(`⚠️ Skipping Prettier formatting for registry file: ${error.message}`);
|
|
990
|
-
return content;
|
|
991
|
-
}
|
|
992
|
-
}
|
|
993
|
-
/**
|
|
994
1028
|
* Updates the registry.ts file with the generated code
|
|
995
1029
|
*/
|
|
996
|
-
|
|
1030
|
+
function updateRegistryFile(registryFilePath, generatedCode) {
|
|
997
1031
|
let existingContent;
|
|
998
1032
|
if (!existsSync(registryFilePath)) {
|
|
999
1033
|
logger.debug("📝 Creating new registry file...");
|
|
@@ -1018,7 +1052,7 @@ export const registry = new ComponentRegistry();
|
|
|
1018
1052
|
const startIndex = existingContent.indexOf(startMarker);
|
|
1019
1053
|
const endIndex = existingContent.indexOf(endMarker);
|
|
1020
1054
|
if (startIndex === -1 || endIndex === -1) throw new Error(`Registry file ${registryFilePath} is missing static registry markers. Please add "${startMarker}" and "${endMarker}" markers to define the generated content area.`);
|
|
1021
|
-
const updatedContent =
|
|
1055
|
+
const updatedContent = formatWithProjectBiome(`${existingContent.slice(0, startIndex + 24)}\n${generatedCode}\n${existingContent.slice(endIndex)}`, registryFilePath);
|
|
1022
1056
|
if (updatedContent === existingContent) {
|
|
1023
1057
|
logger.debug(`⏭️ Registry unchanged, skipping write: ${registryFilePath}`);
|
|
1024
1058
|
return false;
|
|
@@ -1070,7 +1104,7 @@ const staticRegistryPlugin = (config = {}) => {
|
|
|
1070
1104
|
logger.debug(`📦 Found ${components.length} components with @Component decorators`);
|
|
1071
1105
|
const generatedCode = generateRegistryCode(components, registryIdentifier);
|
|
1072
1106
|
const registryFilePath = resolve$1(projectRoot, registryPath);
|
|
1073
|
-
const changed =
|
|
1107
|
+
const changed = updateRegistryFile(registryFilePath, generatedCode);
|
|
1074
1108
|
logger.debug("✅ Static registry generation complete!");
|
|
1075
1109
|
return {
|
|
1076
1110
|
registryFilePath,
|
|
@@ -1410,9 +1444,9 @@ const PASSTHROUGH_QUERY = "?platform-passthrough";
|
|
|
1410
1444
|
* Finds a user-ejected entry file in the app directory.
|
|
1411
1445
|
* Returns the absolute path if found, undefined otherwise.
|
|
1412
1446
|
*/
|
|
1413
|
-
function findUserEntry(appDirectory, basename) {
|
|
1447
|
+
function findUserEntry(appDirectory, basename$1) {
|
|
1414
1448
|
for (const ext of ENTRY_EXTENSIONS) {
|
|
1415
|
-
const filePath = path.resolve(appDirectory, basename + ext);
|
|
1449
|
+
const filePath = path.resolve(appDirectory, basename$1 + ext);
|
|
1416
1450
|
if (fs$1.existsSync(filePath)) return filePath;
|
|
1417
1451
|
}
|
|
1418
1452
|
}
|
|
@@ -1513,8 +1547,8 @@ function platformEntryPlugin() {
|
|
|
1513
1547
|
const watcher = server.watcher;
|
|
1514
1548
|
const checkEntryChange = (filePath) => {
|
|
1515
1549
|
const relative$1 = path.relative(appDir, filePath);
|
|
1516
|
-
const basename = path.basename(relative$1, path.extname(relative$1));
|
|
1517
|
-
if (path.dirname(relative$1) !== "." || basename !== "entry.server" && basename !== "entry.client") return;
|
|
1550
|
+
const basename$1 = path.basename(relative$1, path.extname(relative$1));
|
|
1551
|
+
if (path.dirname(relative$1) !== "." || basename$1 !== "entry.server" && basename$1 !== "entry.client") return;
|
|
1518
1552
|
const ext = path.extname(relative$1);
|
|
1519
1553
|
if (!ENTRY_EXTENSIONS.includes(ext)) return;
|
|
1520
1554
|
const nowHasServer = findUserEntry(appDir, "entry.server") !== void 0;
|