Refactor extension build process to use modular build steps#6867
Refactor extension build process to use modular build steps#6867alfonso-noriega merged 7 commits intomainfrom
Conversation
This stack of pull requests is managed by Graphite. Learn more about stacking. |
64d581f to
c4c3353
Compare
1c5c718 to
3ac919f
Compare
c4c3353 to
d2a2b21
Compare
2b6f7ba to
68c2a9f
Compare
d2a2b21 to
3837d71
Compare
8096aa6 to
f6ae0a0
Compare
794d3e0 to
75deab1
Compare
f6ae0a0 to
1b503e0
Compare
75deab1 to
feab6d2
Compare
1b503e0 to
01cfc8b
Compare
feab6d2 to
27b8cfc
Compare
01cfc8b to
048e70a
Compare
27b8cfc to
34005f5
Compare
048e70a to
c25acc0
Compare
4dd6917 to
56f0d4a
Compare
Coverage report
Test suite run success4000 tests passing in 1531 suites. Report generated by 🧪jest coverage report action from 9c5a652 |
55fc27f to
b5573b3
Compare
f528520 to
60604ec
Compare
packages/app/src/cli/models/extensions/specifications/channel.test.ts
Outdated
Show resolved
Hide resolved
packages/app/src/cli/models/extensions/specifications/ui_extension_build.test.ts
Outdated
Show resolved
Hide resolved
packages/app/src/cli/models/extensions/specifications/ui_extension_build.test.ts
Outdated
Show resolved
Hide resolved
packages/app/src/cli/models/extensions/specifications/channel.test.ts
Outdated
Show resolved
Hide resolved
packages/app/src/cli/models/extensions/specifications/channel.test.ts
Outdated
Show resolved
Hide resolved
Differences in type declarationsWe detected differences in the type declarations generated by Typescript for this branch compared to the baseline ('main' branch). Please, review them to ensure they are backward-compatible. Here are some important things to keep in mind:
New type declarationsWe found no new type declarations in this PR Existing type declarationspackages/cli-kit/dist/public/node/path.d.ts import type { URL } from 'url';
/**
* Joins a list of paths together.
*
* @param paths - Paths to join.
* @returns Joined path.
*/
export declare function joinPath(...paths: string[]): string;
/**
* Normalizes a path.
*
* @param path - Path to normalize.
* @returns Normalized path.
*/
export declare function normalizePath(path: string): string;
/**
* Resolves a list of paths together.
*
* @param paths - Paths to resolve.
* @returns Resolved path.
*/
export declare function resolvePath(...paths: string[]): string;
/**
* Returns the relative path from one path to another.
*
* @param from - Path to resolve from.
* @param to - Path to resolve to.
* @returns Relative path.
*/
export declare function relativePath(from: string, to: string): string;
/**
* Returns whether the path is absolute.
*
* @param path - Path to check.
* @returns Whether the path is absolute.
*/
export declare function isAbsolutePath(path: string): boolean;
/**
* Returns the directory name of a path.
*
* @param path - Path to get the directory name of.
* @returns Directory name.
*/
export declare function dirname(path: string): string;
/**
* Returns the base name of a path.
*
* @param path - Path to get the base name of.
* @param ext - Optional extension to remove from the result.
* @returns Base name.
*/
export declare function basename(path: string, ext?: string): string;
/**
* Returns the extension of the path.
*
* @param path - Path to get the extension of.
* @returns Extension.
*/
export declare function extname(path: string): string;
/**
* Parses a path into its components (root, dir, base, ext, name).
*
* @param path - Path to parse.
* @returns Parsed path object.
*/
export declare function parsePath(path: string): {
root: string;
dir: string;
base: string;
ext: string;
name: string;
};
/**
* Given an absolute filesystem path, it makes it relative to
* the current working directory. This is useful when logging paths
* to allow the users to click on the file and let the OS open it
* in the editor of choice.
*
* @param path - Path to relativize.
* @param dir - Current working directory.
* @returns Relativized path.
*/
export declare function relativizePath(path: string, dir?: string): string;
/**
* Given 2 paths, it returns whether the second path is a subpath of the first path.
*
* @param mainPath - The main path.
* @param subpath - The subpath.
* @returns Whether the subpath is a subpath of the main path.
*/
export declare function isSubpath(mainPath: string, subpath: string): boolean;
/**
* Given a module's import.meta.url it returns the directory containing the module.
*
* @param moduleURL - The value of import.meta.url in the context of the caller module.
* @returns The path to the directory containing the caller module.
*/
export declare function moduleDirectory(moduleURL: string | URL): string;
/**
* When running a script using `npm run`, something interesting happens. If the current
* folder does not have a `package.json` or a `node_modules` folder, npm will traverse
* the directory tree upwards until it finds one. Then it will run the script and set
* `process.cwd()` to that folder, while the actual path is stored in the INIT_CWD
* environment variable (see here: https://docs.npmjs.com/cli/v9/commands/npm-run-script#description).
*
* @returns The path to the current working directory.
*/
export declare function cwd(): string;
/**
* Tries to get the value of the `--path` argument, if provided.
*
* @param argv - The arguments to search for the `--path` argument.
* @returns The value of the `--path` argument, if provided.
*/
export declare function sniffForPath(argv?: string[]): string | undefined;
/**
* Returns whether the `--json` or `-j` flags are present in the arguments.
*
* @param argv - The arguments to search for the `--json` and `-j` flags.
* @returns Whether the `--json` or `-j` flag is present in the arguments.
*/
export declare function sniffForJson(argv?: string[]): boolean;
+/**
+ * Removes any `..` traversal segments from a relative path and calls `warn`
+ * if any were stripped. Normal `..` that cancel out within the path (e.g.
+ * `foo/../bar` → `bar`) are collapsed but never allowed to escape the root.
+ * Both `/` and `\` are treated as separators for cross-platform safety.
+ *
+ * @param input - The relative path to sanitize.
+ * @param warn - Called with a human-readable warning when traversal segments are removed.
+ * @returns The sanitized path (may be an empty string if all segments were traversal).
+ */
+export declare function sanitizeRelativePath(input: string, warn: (msg: string) => void): string;
|
|
The concern raised here is a false alarm after investigation. The two
The result is identical ( |
…ests The build integration tests in channel.test.ts exercised copy behavior already tested by include-assets-step.test.ts. The step-structure and serialization tests in ui_extension_build.test.ts tested implementation details without adding meaningful value. Keeping only the mode/config shape assertions that verify the spec wires up the correct build config. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Keep only 'uses copy_files mode' — the step config shape is covered by include-assets-step tests and doesn't need duplication here. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
After stripping implementation-detail tests only one trivial assertion remained. Removing the file entirely per reviewer feedback. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
These tests were testing implementation details already covered by dedicated unit tests in the build steps layer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>

Wire extension specifications to use client steps
Summary
This PR wires all extension specifications to use the
clientStepscontract introduced in #01-build-steps-infrastructure. Thebuild()method inExtensionInstanceis refactored from a hard-coded mode switch to a generic step execution loop, and every spec now declares its ownclientStepsarray.Depends on:
01-build-steps-infrastructureWhat changed
ExtensionInstance.build()— mode switch replaced by step loopBefore:
After:
The mode switch and all its direct function call imports (
buildThemeExtension,buildUIExtension,buildFunctionExtension,copyFilesForExtension) are removed. The step loop is uniform across all extension types.extension.ts—buildThemeExtensionremovedbuildThemeExtension()and itsrunThemeCheckimport are removed fromextension.ts— the logic now lives exclusively inbuild-theme-step.ts.Specs wired
All specs now declare
clientSteps. ThebuildConfig.modeis kept as a transitional field.UI extensions
Theme
Function
Tax calculation
Flow template
Channel
Tests added
Each spec now has a dedicated build test file asserting:
buildConfig.modeis correctclientStepshas the expected step structureextension.build()produces the correct output on disk (integration)ui_extension_build.test.tsbundle_ui+copy_static_assetsstepstheme.test.tsbuild_theme+bundle_themestepsfunction_build.test.tsbuild_functionstep delegates tobuildFunctionExtensiontax_calculation_build.test.tscreate_tax_stubwrites(()=>{})();flow_template.test.ts.flow/.json/.toml, not.js/.ts; preserves subdirschannel.test.tsspecifications/subdirectory intooutput/specifications/; filters by extensionFiles changed
models/extensions/extension-instance.tsbuild()rewritten to useexecuteSteploopservices/build/extension.tsbuildThemeExtension+runThemeCheckimportspecifications/ui_extension.tsclientStepsspecifications/checkout_post_purchase.tsclientStepsspecifications/checkout_ui_extension.tsclientStepsspecifications/pos_ui_extension.tsclientStepsspecifications/product_subscription.tsclientStepsspecifications/web_pixel_extension.tsclientStepsspecifications/theme.tsclientStepsspecifications/function.tsclientStepsspecifications/tax_calculation.tsclientStepsspecifications/flow_template.tsclientStepswithinclude_assetsconfigspecifications/channel.tsinclude_assetsscoped tospecifications/subdirectoryMeasuring impact
How do we know this change was effective? Please choose one:
Checklist