@salesforce/storefront-next-dev 1.3.1 → 1.4.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 +22 -9
- package/dist/bundle.js +16 -9
- package/dist/cartridge-services/index.js +50 -7
- package/dist/cartridge-services/index.js.map +1 -1
- package/dist/commands/config/inspect.js +20 -12
- package/dist/commands/create-storefront.js +6 -1
- package/dist/commands/deploy-cartridge.js +14 -4
- package/dist/commands/push.js +132 -37
- package/dist/entry/server.js +25 -2
- package/dist/entry/server.js.map +1 -1
- package/dist/flags.js +18 -1
- package/dist/generate-cartridge.js +25 -5
- package/dist/hooks/init.js +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +61 -13
- package/dist/index.js.map +1 -1
- package/dist/logger/index.d.ts +2 -1
- package/dist/logger/index.d.ts.map +1 -1
- package/dist/logger/index.js +25 -2
- package/dist/logger/index.js.map +1 -1
- package/dist/logger.js +26 -3
- package/dist/mrt/ssr.mjs +8 -8
- package/dist/mrt/ssr.mjs.map +1 -1
- package/dist/mrt/streamingHandler.mjs +25 -25
- package/dist/mrt/streamingHandler.mjs.map +1 -1
- package/dist/utils.js +2 -1
- package/package.json +10 -10
package/README.md
CHANGED
|
@@ -149,18 +149,31 @@ Run `pnpm sfnext push --help` to see all available options:
|
|
|
149
149
|
|
|
150
150
|
- `-b, --build-directory <dir>`: Build directory to push (default: auto-detected)
|
|
151
151
|
- `-m, --message <message>`: Bundle message (default: git branch:commit)
|
|
152
|
-
- `-p, --project <slug
|
|
153
|
-
- `-e, --environment <
|
|
152
|
+
- `-p, --project <slug>` (alias `-s, --storefront`): Project slug on Managed Runtime (env: `MRT_PROJECT`)
|
|
153
|
+
- `-e, --environment <env>` (alias `-t, --target`): Environment to deploy to (env: `MRT_ENVIRONMENT`; `MRT_TARGET` also accepted)
|
|
154
154
|
- `-w, --wait`: Wait for deployment to complete
|
|
155
|
-
- `--
|
|
156
|
-
- `--
|
|
157
|
-
-
|
|
155
|
+
- `--mrt-backend <legacy|scapi|auto>`: MRT API to use (env: `MRT_BACKEND`, default `legacy`). See [MRT backends](#mrt-backends).
|
|
156
|
+
- `--api-key <api-key>`: MRT API key (legacy backend; env: `MRT_API_KEY`)
|
|
157
|
+
- `-u, --cloud-origin <origin>` (alias `-o`): MRT API origin (legacy backend; default: https://cloud.mobify.com)
|
|
158
|
+
- `-c, --credentials-file <file>`: Credentials file location (legacy backend; default: `~/.mobify`)
|
|
158
159
|
|
|
159
|
-
|
|
160
|
+
`--project-slug` is still accepted as a deprecated alias for `--project`.
|
|
160
161
|
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
162
|
+
#### MRT backends
|
|
163
|
+
|
|
164
|
+
`push` and `config inspect` can talk to Managed Runtime through either API:
|
|
165
|
+
|
|
166
|
+
| `--mrt-backend` | Behavior |
|
|
167
|
+
| --- | --- |
|
|
168
|
+
| `legacy` (default) | MRT Cloud API with an MRT API key. |
|
|
169
|
+
| `scapi` | SCAPI with Account Manager OAuth. Errors if the prerequisites are missing. |
|
|
170
|
+
| `auto` | SCAPI when its prerequisites are configured, otherwise legacy. Falls back to legacy on safe pre-request errors. |
|
|
171
|
+
|
|
172
|
+
The SCAPI backend needs `SFCC_CLIENT_ID`, `SFCC_CLIENT_SECRET`, `SFCC_SHORTCODE`, and `SFCC_TENANT_ID`, and an API client with the `sfcc.storefront.deployments.rw` scope (`push`) or `sfcc.storefront.environments` (`config inspect`). See [MRT Backends](https://salesforcecommercecloud.github.io/b2c-developer-tooling/cli/mrt.html#mrt-backends).
|
|
173
|
+
|
|
174
|
+
#### Deploy cartridge
|
|
175
|
+
|
|
176
|
+
`deploy-cartridge` looks up and reloads the active code version through SCAPI (`sfcc.scripts` / `sfcc.scripts.rw` scopes) when `SFCC_SHORTCODE` and `SFCC_TENANT_ID` are set, and falls back to OCAPI otherwise. Force one with `--api-backend scapi|ocapi` (env: `SFCC_API_BACKEND`).
|
|
164
177
|
|
|
165
178
|
#### Manage extensions
|
|
166
179
|
|
package/dist/bundle.js
CHANGED
|
@@ -9,6 +9,20 @@ import archiver from "archiver";
|
|
|
9
9
|
/**
|
|
10
10
|
* Create a bundle from the build directory
|
|
11
11
|
*/
|
|
12
|
+
/**
|
|
13
|
+
* Dependencies recorded in bundle metadata: the project's declared dependencies
|
|
14
|
+
* plus the resolved versions of PWA Kit / Storefront Next packages.
|
|
15
|
+
*/
|
|
16
|
+
const getBundleDependencies = (projectDirectory) => {
|
|
17
|
+
const { dependencies = {}, devDependencies = {} } = getProjectPkg(projectDirectory);
|
|
18
|
+
const dependencyTree = getProjectDependencyTree(projectDirectory);
|
|
19
|
+
const pwaKitDeps = dependencyTree ? getPwaKitDependencies(dependencyTree) : {};
|
|
20
|
+
return {
|
|
21
|
+
...dependencies,
|
|
22
|
+
...devDependencies,
|
|
23
|
+
...pwaKitDeps
|
|
24
|
+
};
|
|
25
|
+
};
|
|
12
26
|
const createBundle = async (options) => {
|
|
13
27
|
const { message, ssr_parameters, ssr_only, ssr_shared, buildDirectory, projectDirectory, projectSlug } = options;
|
|
14
28
|
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "storefront-next-dev-push-"));
|
|
@@ -38,14 +52,7 @@ const createBundle = async (options) => {
|
|
|
38
52
|
archive.on("error", reject);
|
|
39
53
|
output.on("finish", () => {
|
|
40
54
|
try {
|
|
41
|
-
const
|
|
42
|
-
const dependencyTree = getProjectDependencyTree(projectDirectory);
|
|
43
|
-
const pwaKitDeps = dependencyTree ? getPwaKitDependencies(dependencyTree) : {};
|
|
44
|
-
const bundle_metadata = { dependencies: {
|
|
45
|
-
...dependencies,
|
|
46
|
-
...devDependencies,
|
|
47
|
-
...pwaKitDeps
|
|
48
|
-
} };
|
|
55
|
+
const bundle_metadata = { dependencies: getBundleDependencies(projectDirectory) };
|
|
49
56
|
const data = fs.readFileSync(destination);
|
|
50
57
|
const encoding = "base64";
|
|
51
58
|
fs.rmSync(tmpDir, { recursive: true });
|
|
@@ -80,4 +87,4 @@ const createBundle = async (options) => {
|
|
|
80
87
|
};
|
|
81
88
|
|
|
82
89
|
//#endregion
|
|
83
|
-
export { createBundle as t };
|
|
90
|
+
export { getBundleDependencies as n, createBundle as t };
|
|
@@ -19,6 +19,27 @@ const LEVEL_PRIORITY = {
|
|
|
19
19
|
info: 2,
|
|
20
20
|
debug: 3
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Type guard for a {@link LogLevel} name. Uses `Object.hasOwn` (not `in`) so
|
|
24
|
+
* inherited properties like `toString` don't falsely validate an env value.
|
|
25
|
+
*/
|
|
26
|
+
function isLogLevel(value) {
|
|
27
|
+
return Object.hasOwn(LEVEL_PRIORITY, value);
|
|
28
|
+
}
|
|
29
|
+
/**
|
|
30
|
+
* Managed Runtime injects `MRT_LOG_LEVEL` as a numeric string from its own
|
|
31
|
+
* `LogLevel` enum (`0=TRACE, 1=DEBUG, 2=INFO, 3=WARN, 4=ERROR, 5=FATAL`), not a
|
|
32
|
+
* level name — so it must be mapped, not matched against {@link LEVEL_PRIORITY}'s
|
|
33
|
+
* keys. `TRACE` and `FATAL` clamp to the closest level this logger supports.
|
|
34
|
+
*/
|
|
35
|
+
const MRT_NUMERIC_TO_LEVEL = {
|
|
36
|
+
"0": "debug",
|
|
37
|
+
"1": "debug",
|
|
38
|
+
"2": "info",
|
|
39
|
+
"3": "warn",
|
|
40
|
+
"4": "error",
|
|
41
|
+
"5": "error"
|
|
42
|
+
};
|
|
22
43
|
let overrideLevel;
|
|
23
44
|
/**
|
|
24
45
|
* Returns true when the `DEBUG` env var targets sfnext or is a general enable flag.
|
|
@@ -42,8 +63,10 @@ function debugEnablesSfnext() {
|
|
|
42
63
|
}
|
|
43
64
|
function resolveLevel() {
|
|
44
65
|
if (overrideLevel) return overrideLevel;
|
|
45
|
-
const
|
|
46
|
-
if (
|
|
66
|
+
const mrtLevel = process.env.MRT_LOG_LEVEL;
|
|
67
|
+
if (mrtLevel && mrtLevel in MRT_NUMERIC_TO_LEVEL) return MRT_NUMERIC_TO_LEVEL[mrtLevel];
|
|
68
|
+
const sfccLevel = process.env.SFCC_LOG_LEVEL;
|
|
69
|
+
if (sfccLevel && isLogLevel(sfccLevel)) return sfccLevel;
|
|
47
70
|
if (debugEnablesSfnext()) return "debug";
|
|
48
71
|
if (process.env.NODE_ENV === "production") return "warn";
|
|
49
72
|
return "info";
|
|
@@ -79,6 +102,23 @@ const logger = {
|
|
|
79
102
|
//#endregion
|
|
80
103
|
//#region src/cartridge-services/react-router-config.ts
|
|
81
104
|
let isCliAvailable = null;
|
|
105
|
+
function filePathEndsWithRoute(filePath, routeFile) {
|
|
106
|
+
const routeFileNormalized = routeFile.replace(/^\.\//, "");
|
|
107
|
+
return filePath === routeFileNormalized || filePath.endsWith(`/${routeFileNormalized}`);
|
|
108
|
+
}
|
|
109
|
+
/**
|
|
110
|
+
* Match the selected vertical route shadow to its canonical route module.
|
|
111
|
+
*
|
|
112
|
+
* `VERTICAL` is optional and selects the overlay directory reported by React Router,
|
|
113
|
+
* for example `furniture` for `verticals/furniture/routes/_app._index.tsx`.
|
|
114
|
+
*/
|
|
115
|
+
function routeFileMatches(filePath, routeFile) {
|
|
116
|
+
if (filePathEndsWithRoute(filePath, routeFile)) return true;
|
|
117
|
+
const vertical = process.env.VERTICAL;
|
|
118
|
+
const verticalRoutePrefix = vertical ? `verticals/${vertical}/routes/` : void 0;
|
|
119
|
+
if (!verticalRoutePrefix || !routeFile.startsWith(verticalRoutePrefix)) return false;
|
|
120
|
+
return filePathEndsWithRoute(filePath, `routes/${routeFile.slice(verticalRoutePrefix.length)}`);
|
|
121
|
+
}
|
|
82
122
|
function checkReactRouterCli(projectDirectory) {
|
|
83
123
|
if (isCliAvailable !== null) return isCliAvailable;
|
|
84
124
|
try {
|
|
@@ -132,7 +172,7 @@ function getReactRouterRoutes(projectDirectory) {
|
|
|
132
172
|
* This ensures we get the exact same route resolution as React Router uses internally.
|
|
133
173
|
* @param filePath - Absolute path to the route file
|
|
134
174
|
* @param projectRoot - The project root directory
|
|
135
|
-
* @returns The route path (e.g., '/cart', '/
|
|
175
|
+
* @returns The route path (e.g., '/cart', '/p/*')
|
|
136
176
|
* @example
|
|
137
177
|
* const route = filePathToRoute('/path/to/project/src/routes/_app.cart.tsx', '/path/to/project');
|
|
138
178
|
* // Returns: '/cart'
|
|
@@ -140,10 +180,13 @@ function getReactRouterRoutes(projectDirectory) {
|
|
|
140
180
|
function filePathToRoute(filePath, projectRoot) {
|
|
141
181
|
const filePathPosix = filePath.replace(/\\/g, "/");
|
|
142
182
|
const canonicalRoutes = flattenRoutes(getReactRouterRoutes(projectRoot)).filter((route) => !route.id.endsWith("--root-duplicate"));
|
|
143
|
-
for (const route of canonicalRoutes) {
|
|
144
|
-
const
|
|
145
|
-
|
|
146
|
-
|
|
183
|
+
for (const route of canonicalRoutes) if (routeFileMatches(filePathPosix, route.file.replace(/\\/g, "/"))) {
|
|
184
|
+
const seoAliases = canonicalRoutes.filter((candidate) => candidate.id.startsWith(`${route.id}--seo-alias--`)).sort((left, right) => left.path.localeCompare(right.path));
|
|
185
|
+
if (seoAliases.length > 0) {
|
|
186
|
+
if (seoAliases.length > 1) logger.warn(`Multiple SEO route aliases match ${filePath}; using ${seoAliases[0].path} for Page Designer metadata.`);
|
|
187
|
+
return seoAliases[0].path;
|
|
188
|
+
}
|
|
189
|
+
return route.path;
|
|
147
190
|
}
|
|
148
191
|
logger.warn(`Could not find route for file: ${filePath}`);
|
|
149
192
|
return "/unknown";
|