@woodsportal/hubspot-kit 1.0.2
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/CHANGELOG.md +31 -0
- package/LICENSE +21 -0
- package/README.md +62 -0
- package/SECURITY.md +27 -0
- package/dist/chunk-JDGIAYHX.js +87 -0
- package/dist/chunk-JDGIAYHX.js.map +1 -0
- package/dist/chunk-U4KFGTEW.js +136 -0
- package/dist/chunk-U4KFGTEW.js.map +1 -0
- package/dist/cli.js +1849 -0
- package/dist/cli.js.map +1 -0
- package/dist/exec-K5C6LZP4.js +10 -0
- package/dist/exec-K5C6LZP4.js.map +1 -0
- package/dist/index.d.ts +228 -0
- package/dist/index.js +204 -0
- package/dist/index.js.map +1 -0
- package/dist/vite/index.d.ts +17 -0
- package/dist/vite/index.js +9 -0
- package/dist/vite/index.js.map +1 -0
- package/examples/README.md +12 -0
- package/examples/fixtures/adopted-module-hybrid/public/fields.json +1 -0
- package/examples/fixtures/adopted-module-hybrid/public/module.html +1 -0
- package/examples/fixtures/adopted-module-hybrid/woodscli.json +7 -0
- package/examples/fixtures/adopted-module-monolith/public/fields.json +1 -0
- package/examples/fixtures/adopted-module-monolith/woodscli.json +7 -0
- package/examples/fixtures/adopted-theme-minimal/src/theme/templates/page.html +1 -0
- package/examples/fixtures/adopted-theme-minimal/src/theme/theme.json +1 -0
- package/examples/fixtures/adopted-theme-minimal/woodscli.json +7 -0
- package/examples/fixtures/existing-module-hybrid/public/fields.json +1 -0
- package/examples/fixtures/existing-module-hybrid/public/module.html +1 -0
- package/examples/fixtures/existing-theme-minimal/templates/page.html +1 -0
- package/examples/fixtures/existing-theme-minimal/theme.json +1 -0
- package/examples/greenfield/module-hybrid-cdn/README.md +9 -0
- package/examples/greenfield/module-hybrid-cdn/index.html +1 -0
- package/examples/greenfield/module-hybrid-cdn/package.json +14 -0
- package/examples/greenfield/module-hybrid-cdn/public/fields.json +1 -0
- package/examples/greenfield/module-hybrid-cdn/public/module.html +1 -0
- package/examples/greenfield/module-hybrid-cdn/src/main.tsx +2 -0
- package/examples/greenfield/module-hybrid-cdn/vite.config.dev.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn/vite.config.hubspot.mjs +1 -0
- package/examples/greenfield/module-hybrid-cdn/woodscli.json +8 -0
- package/examples/greenfield/module-hybrid-cdn/wphs.adapter.ts +7 -0
- package/examples/greenfield/module-monolith/package.json +12 -0
- package/examples/greenfield/module-monolith/public/fields.json +1 -0
- package/examples/greenfield/module-monolith/vite.config.dev.mjs +1 -0
- package/examples/greenfield/module-monolith/vite.config.monolith.mjs +1 -0
- package/examples/greenfield/module-monolith/woodscli.json +25 -0
- package/examples/greenfield/module-monolith/wphs.adapter.ts +7 -0
- package/examples/greenfield/theme-starter/package.json +11 -0
- package/examples/greenfield/theme-starter/src/theme/css/main.css +1 -0
- package/examples/greenfield/theme-starter/src/theme/templates/base.html +1 -0
- package/examples/greenfield/theme-starter/src/theme/templates/page.html +1 -0
- package/examples/greenfield/theme-starter/src/theme/theme.json +1 -0
- package/examples/greenfield/theme-starter/theme.json +6 -0
- package/examples/greenfield/theme-starter/vite.config.dev.mjs +1 -0
- package/examples/greenfield/theme-starter/woodscli.json +7 -0
- package/examples/manifest.json +20 -0
- package/examples/smoke/run-matrix.mjs +100 -0
- package/package.json +94 -0
- package/scripts/audit-module-config-fields.mjs +73 -0
- package/scripts/audit-parity.mjs +22 -0
- package/scripts/audit-tdz-split.mjs +111 -0
- package/scripts/build-cdn.mjs +36 -0
- package/scripts/check-hubspot-bundle-size.mjs +58 -0
- package/scripts/config-files.mjs +53 -0
- package/scripts/detect-layout.mjs +70 -0
- package/scripts/ensure-wphs-workspace.mjs +53 -0
- package/scripts/generate-cdn-manifest.mjs +92 -0
- package/scripts/generate-module-config-example.mjs +57 -0
- package/scripts/hubspot-module-postbuild.mjs +151 -0
- package/scripts/load-portal-cdn-config.mjs +91 -0
- package/scripts/migrate-sdk-3-api.mjs +144 -0
- package/scripts/module-config-dir.mjs +12 -0
- package/scripts/portal-env-mode.mjs +24 -0
- package/scripts/portal-vite-env.mjs +39 -0
- package/scripts/project-root.mjs +30 -0
- package/scripts/publish-cdn-github.mjs +273 -0
- package/scripts/read-woodsportal-config.mjs +40 -0
- package/scripts/resolve-cdn-base-url.mjs +32 -0
- package/scripts/resolve-consumer-dep.mjs +25 -0
- package/scripts/resolve-portal-build-id.mjs +67 -0
- package/scripts/run-dev.mjs +93 -0
- package/scripts/run-hubspot-module-postbuild.mjs +19 -0
- package/scripts/run-vite-build.mjs +33 -0
- package/scripts/tailwind/prefix-plugin.js +291 -0
- package/scripts/tailwind/prefix.env.js +11 -0
- package/scripts/tailwind/tailwind-content-plugin.js +27 -0
- package/scripts/tailwind/tailwind-content.js +267 -0
- package/scripts/verify-cdn-api-endpoint.mjs +68 -0
- package/scripts/vite/ensure-dev-kit-shims.js +49 -0
- package/scripts/vite/ensure-dev-module-config-shim.js +1 -0
- package/scripts/vite/module-config-store.js +242 -0
- package/scripts/vite/plugins.d.ts +9 -0
- package/scripts/vite/portal-build-shared.js +110 -0
- package/scripts/vite/react-dev-plugin-options.js +21 -0
- package/scripts/vite/resolve-from-project-root.js +31 -0
- package/scripts/vite/sdk-log-bridge.js +104 -0
- package/scripts/watch-hubspot.mjs +86 -0
- package/scripts/wphs-paths.mjs +115 -0
- package/scripts/write-audit-artifacts.mjs +56 -0
- package/templates/hubspot.config.yml.example +10 -0
- package/templates/module-hybrid-cdn/README.md +9 -0
- package/templates/module-hybrid-cdn/hubspot.config.yml.example +10 -0
- package/templates/module-hybrid-cdn/index.html +1 -0
- package/templates/module-hybrid-cdn/package.json +14 -0
- package/templates/module-hybrid-cdn/public/fields.json +1 -0
- package/templates/module-hybrid-cdn/public/module.html +1 -0
- package/templates/module-hybrid-cdn/src/main.tsx +2 -0
- package/templates/module-hybrid-cdn/vite.config.dev.mjs +1 -0
- package/templates/module-hybrid-cdn/vite.config.hubspot.mjs +1 -0
- package/templates/module-hybrid-cdn/woodscli.json +8 -0
- package/templates/module-hybrid-cdn/wphs.adapter.ts +7 -0
- package/templates/module-monolith/hubspot.config.yml.example +10 -0
- package/templates/module-monolith/package.json +12 -0
- package/templates/module-monolith/public/fields.json +1 -0
- package/templates/module-monolith/vite.config.dev.mjs +1 -0
- package/templates/module-monolith/vite.config.monolith.mjs +1 -0
- package/templates/module-monolith/woodscli.json +25 -0
- package/templates/module-monolith/wphs.adapter.ts +7 -0
- package/templates/theme-starter/hubspot.config.yml.example +10 -0
- package/templates/theme-starter/package.json +11 -0
- package/templates/theme-starter/src/theme/css/main.css +1 -0
- package/templates/theme-starter/src/theme/templates/base.html +1 -0
- package/templates/theme-starter/src/theme/templates/page.html +1 -0
- package/templates/theme-starter/src/theme/theme.json +1 -0
- package/templates/theme-starter/theme.json +6 -0
- package/templates/theme-starter/vite.config.dev.mjs +1 -0
- package/templates/theme-starter/woodscli.json +7 -0
- package/vite/vite.config.audit.mjs +33 -0
- package/vite/vite.config.cdn-app.mjs +44 -0
- package/vite/vite.config.cdn-esm.mjs +35 -0
- package/vite/vite.config.cdn-vendor.mjs +33 -0
- package/vite/vite.config.dev.mjs +62 -0
- package/vite/vite.config.hubspot.mjs +33 -0
- package/vite/vite.config.monolith.mjs +55 -0
- package/vite/vite.config.tdz-audit.mjs +58 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
import {
|
|
2
|
+
ExitCode,
|
|
3
|
+
WOODSCLI_CONFIG_FILE,
|
|
4
|
+
WpHsError,
|
|
5
|
+
createViteConfig,
|
|
6
|
+
findProjectRoot,
|
|
7
|
+
getKitRoot,
|
|
8
|
+
getViteConfigPath,
|
|
9
|
+
kitPath,
|
|
10
|
+
resolveProjectConfig
|
|
11
|
+
} from "./chunk-U4KFGTEW.js";
|
|
12
|
+
|
|
13
|
+
// src/schema/woodsportal-hs.ts
|
|
14
|
+
import { z } from "zod";
|
|
15
|
+
var CONFIG_VERSION = 1;
|
|
16
|
+
var WPHS_WORKSPACE_DIR = ".wphs";
|
|
17
|
+
var WoodsPortalHsSchema = z.object({
|
|
18
|
+
configVersion: z.number().int().default(CONFIG_VERSION),
|
|
19
|
+
projectType: z.enum(["module-hybrid-cdn", "module-monolith", "theme-starter"]),
|
|
20
|
+
module: z.object({
|
|
21
|
+
name: z.string().min(1),
|
|
22
|
+
uploadPath: z.string().min(1),
|
|
23
|
+
outDir: z.string().default("dist/WoodsPortal.Module")
|
|
24
|
+
}),
|
|
25
|
+
theme: z.object({
|
|
26
|
+
name: z.string().min(1),
|
|
27
|
+
uploadPath: z.string().min(1),
|
|
28
|
+
srcDir: z.string().default("src/theme"),
|
|
29
|
+
themeJson: z.string().default("src/theme/theme.json")
|
|
30
|
+
}).optional(),
|
|
31
|
+
cdn: z.object({
|
|
32
|
+
mode: z.enum(["jsdelivr", "custom", "local", "none"]).default("jsdelivr"),
|
|
33
|
+
buildIdFile: z.string().default(".portal-build-id"),
|
|
34
|
+
mirror: z.string().default("portal.cdn.json")
|
|
35
|
+
}).default({}),
|
|
36
|
+
entries: z.object({
|
|
37
|
+
app: z.string().default("src/cdn/app-entry.ts"),
|
|
38
|
+
vendor: z.string().default("src/cdn/vendor-entry.ts"),
|
|
39
|
+
hubspotBootstrap: z.string().default("src/hubspot/bootstrap.ts"),
|
|
40
|
+
localDev: z.string().default("src/main.tsx")
|
|
41
|
+
}).default({}),
|
|
42
|
+
env: z.object({
|
|
43
|
+
tiers: z.array(z.enum(["dev", "stg", "prod"])).default(["dev", "stg", "prod"])
|
|
44
|
+
}).default({}),
|
|
45
|
+
dev: z.object({
|
|
46
|
+
moduleConfigDir: z.literal(WPHS_WORKSPACE_DIR).default(WPHS_WORKSPACE_DIR),
|
|
47
|
+
adapter: z.string().default("./wphs.adapter.ts"),
|
|
48
|
+
fieldsJson: z.string().default("public/fields.json"),
|
|
49
|
+
themeFieldsJson: z.string().optional()
|
|
50
|
+
}).default({})
|
|
51
|
+
});
|
|
52
|
+
var DEFAULT_WOODSPORTAL_HS = {
|
|
53
|
+
configVersion: CONFIG_VERSION,
|
|
54
|
+
projectType: "module-hybrid-cdn",
|
|
55
|
+
module: {
|
|
56
|
+
name: "woodsportal-module",
|
|
57
|
+
uploadPath: "dw-modules/woodsportal-module.module",
|
|
58
|
+
outDir: "dist/WoodsPortal.Module"
|
|
59
|
+
},
|
|
60
|
+
cdn: {
|
|
61
|
+
mode: "jsdelivr",
|
|
62
|
+
buildIdFile: ".portal-build-id",
|
|
63
|
+
mirror: "portal.cdn.json"
|
|
64
|
+
},
|
|
65
|
+
entries: {
|
|
66
|
+
app: "src/cdn/app-entry.ts",
|
|
67
|
+
vendor: "src/cdn/vendor-entry.ts",
|
|
68
|
+
hubspotBootstrap: "src/hubspot/bootstrap.ts",
|
|
69
|
+
localDev: "src/main.tsx"
|
|
70
|
+
},
|
|
71
|
+
env: {
|
|
72
|
+
tiers: ["dev", "stg", "prod"]
|
|
73
|
+
},
|
|
74
|
+
dev: {
|
|
75
|
+
moduleConfigDir: WPHS_WORKSPACE_DIR,
|
|
76
|
+
adapter: "./wphs.adapter.ts",
|
|
77
|
+
fieldsJson: "public/fields.json"
|
|
78
|
+
}
|
|
79
|
+
};
|
|
80
|
+
function isModuleProject(config) {
|
|
81
|
+
return config.projectType === "module-hybrid-cdn" || config.projectType === "module-monolith";
|
|
82
|
+
}
|
|
83
|
+
function isThemeProject(config) {
|
|
84
|
+
return config.projectType === "theme-starter";
|
|
85
|
+
}
|
|
86
|
+
function usesCdnPipeline(config) {
|
|
87
|
+
return config.projectType === "module-hybrid-cdn" && config.cdn.mode !== "none";
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
// src/lib/config.ts
|
|
91
|
+
import { readFileSync } from "fs";
|
|
92
|
+
function loadConfig(projectRoot) {
|
|
93
|
+
const root = projectRoot ?? findProjectRoot();
|
|
94
|
+
const resolved = resolveProjectConfig(root);
|
|
95
|
+
if (!resolved) {
|
|
96
|
+
throw new WpHsError(`Missing ${WOODSCLI_CONFIG_FILE}`, {
|
|
97
|
+
exitCode: ExitCode.USER_ERROR,
|
|
98
|
+
remediation: "Run wp init or wp adopt. Legacy woodsportal.hs.json \u2192 run wp migrate --yes."
|
|
99
|
+
});
|
|
100
|
+
}
|
|
101
|
+
let raw;
|
|
102
|
+
try {
|
|
103
|
+
raw = JSON.parse(readFileSync(resolved.path, "utf8"));
|
|
104
|
+
} catch (cause) {
|
|
105
|
+
throw new WpHsError(`Invalid JSON in ${resolved.fileName}`, {
|
|
106
|
+
exitCode: ExitCode.USER_ERROR,
|
|
107
|
+
cause
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
const parsed = WoodsPortalHsSchema.safeParse(raw);
|
|
111
|
+
if (!parsed.success) {
|
|
112
|
+
throw new WpHsError(`${resolved.fileName} validation failed: ${parsed.error.message}`, {
|
|
113
|
+
exitCode: ExitCode.USER_ERROR,
|
|
114
|
+
remediation: "Run wp config validate for details."
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
return {
|
|
118
|
+
root,
|
|
119
|
+
config: parsed.data,
|
|
120
|
+
configPath: resolved.path,
|
|
121
|
+
configFileName: resolved.fileName
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
function validateConfigFile(projectRoot) {
|
|
125
|
+
try {
|
|
126
|
+
const { config } = loadConfig(projectRoot);
|
|
127
|
+
return { valid: true, errors: [], config };
|
|
128
|
+
} catch (error) {
|
|
129
|
+
if (error instanceof WpHsError) {
|
|
130
|
+
return { valid: false, errors: [error.message] };
|
|
131
|
+
}
|
|
132
|
+
return { valid: false, errors: [String(error)] };
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
// src/adapters/types.ts
|
|
137
|
+
var MODULE_CONFIG_CHANGED = "woodsportal:module-config-changed";
|
|
138
|
+
function bindAdapterToModuleConfig(adapter) {
|
|
139
|
+
if (typeof globalThis.window === "undefined") return;
|
|
140
|
+
globalThis.window.addEventListener(MODULE_CONFIG_CHANGED, (event) => {
|
|
141
|
+
const detail = event.detail;
|
|
142
|
+
adapter.syncLiveData(detail.built);
|
|
143
|
+
});
|
|
144
|
+
}
|
|
145
|
+
function syncAdapterFromModuleState(adapter, state) {
|
|
146
|
+
adapter.syncLiveData(state.built);
|
|
147
|
+
}
|
|
148
|
+
var defaultHubSpotKitAdapter = {
|
|
149
|
+
syncLiveData(built) {
|
|
150
|
+
const w = globalThis;
|
|
151
|
+
w.__WPHS_DATA__ = built;
|
|
152
|
+
},
|
|
153
|
+
getDefaultHubSpotData() {
|
|
154
|
+
return {};
|
|
155
|
+
}
|
|
156
|
+
};
|
|
157
|
+
|
|
158
|
+
// src/lib/generic-field-mapper.ts
|
|
159
|
+
function flattenField(field, values, out) {
|
|
160
|
+
const key = field.name;
|
|
161
|
+
const value = values[key];
|
|
162
|
+
if (field.type === "group" && field.children && value && typeof value === "object") {
|
|
163
|
+
const groupValues = value;
|
|
164
|
+
for (const child of field.children) {
|
|
165
|
+
flattenField(child, groupValues, out);
|
|
166
|
+
}
|
|
167
|
+
return;
|
|
168
|
+
}
|
|
169
|
+
if (field.type === "repeater" && Array.isArray(value)) {
|
|
170
|
+
out[key] = value;
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
out[key] = value ?? field.default ?? null;
|
|
174
|
+
}
|
|
175
|
+
function mapFieldsToRuntime(schema, values) {
|
|
176
|
+
const out = {};
|
|
177
|
+
for (const field of schema) {
|
|
178
|
+
flattenField(field, values, out);
|
|
179
|
+
}
|
|
180
|
+
return out;
|
|
181
|
+
}
|
|
182
|
+
export {
|
|
183
|
+
CONFIG_VERSION,
|
|
184
|
+
DEFAULT_WOODSPORTAL_HS,
|
|
185
|
+
ExitCode,
|
|
186
|
+
WPHS_WORKSPACE_DIR,
|
|
187
|
+
WoodsPortalHsSchema,
|
|
188
|
+
WpHsError,
|
|
189
|
+
bindAdapterToModuleConfig,
|
|
190
|
+
createViteConfig,
|
|
191
|
+
defaultHubSpotKitAdapter,
|
|
192
|
+
findProjectRoot,
|
|
193
|
+
getKitRoot,
|
|
194
|
+
getViteConfigPath,
|
|
195
|
+
isModuleProject,
|
|
196
|
+
isThemeProject,
|
|
197
|
+
kitPath,
|
|
198
|
+
loadConfig,
|
|
199
|
+
mapFieldsToRuntime,
|
|
200
|
+
syncAdapterFromModuleState,
|
|
201
|
+
usesCdnPipeline,
|
|
202
|
+
validateConfigFile
|
|
203
|
+
};
|
|
204
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/schema/woodsportal-hs.ts","../src/lib/config.ts","../src/adapters/types.ts","../src/lib/generic-field-mapper.ts"],"sourcesContent":["import { z } from 'zod'\n\nexport const CONFIG_VERSION = 1\n\nexport const WPHS_WORKSPACE_DIR = '.wphs' as const\n\nexport const WoodsPortalHsSchema = z.object({\n configVersion: z.number().int().default(CONFIG_VERSION),\n projectType: z.enum(['module-hybrid-cdn', 'module-monolith', 'theme-starter']),\n module: z.object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n outDir: z.string().default('dist/WoodsPortal.Module'),\n }),\n theme: z\n .object({\n name: z.string().min(1),\n uploadPath: z.string().min(1),\n srcDir: z.string().default('src/theme'),\n themeJson: z.string().default('src/theme/theme.json'),\n })\n .optional(),\n cdn: z\n .object({\n mode: z.enum(['jsdelivr', 'custom', 'local', 'none']).default('jsdelivr'),\n buildIdFile: z.string().default('.portal-build-id'),\n mirror: z.string().default('portal.cdn.json'),\n })\n .default({}),\n entries: z\n .object({\n app: z.string().default('src/cdn/app-entry.ts'),\n vendor: z.string().default('src/cdn/vendor-entry.ts'),\n hubspotBootstrap: z.string().default('src/hubspot/bootstrap.ts'),\n localDev: z.string().default('src/main.tsx'),\n })\n .default({}),\n env: z\n .object({\n tiers: z.array(z.enum(['dev', 'stg', 'prod'])).default(['dev', 'stg', 'prod']),\n })\n .default({}),\n dev: z\n .object({\n moduleConfigDir: z.literal(WPHS_WORKSPACE_DIR).default(WPHS_WORKSPACE_DIR),\n adapter: z.string().default('./wphs.adapter.ts'),\n fieldsJson: z.string().default('public/fields.json'),\n themeFieldsJson: z.string().optional(),\n })\n .default({}),\n})\n\nexport type WoodsPortalHsConfig = z.infer<typeof WoodsPortalHsSchema>\n\nexport const DEFAULT_WOODSPORTAL_HS: WoodsPortalHsConfig = {\n configVersion: CONFIG_VERSION,\n projectType: 'module-hybrid-cdn',\n module: {\n name: 'woodsportal-module',\n uploadPath: 'dw-modules/woodsportal-module.module',\n outDir: 'dist/WoodsPortal.Module',\n },\n cdn: {\n mode: 'jsdelivr',\n buildIdFile: '.portal-build-id',\n mirror: 'portal.cdn.json',\n },\n entries: {\n app: 'src/cdn/app-entry.ts',\n vendor: 'src/cdn/vendor-entry.ts',\n hubspotBootstrap: 'src/hubspot/bootstrap.ts',\n localDev: 'src/main.tsx',\n },\n env: {\n tiers: ['dev', 'stg', 'prod'],\n },\n dev: {\n moduleConfigDir: WPHS_WORKSPACE_DIR,\n adapter: './wphs.adapter.ts',\n fieldsJson: 'public/fields.json',\n },\n}\n\nexport function isModuleProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' || config.projectType === 'module-monolith'\n}\n\nexport function isThemeProject(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'theme-starter'\n}\n\nexport function usesCdnPipeline(config: WoodsPortalHsConfig): boolean {\n return config.projectType === 'module-hybrid-cdn' && config.cdn.mode !== 'none'\n}\n","import { readFileSync } from 'node:fs'\n\nimport {\n resolveProjectConfig,\n WOODSCLI_CONFIG_FILE,\n} from './config-files.js'\nimport { WoodsPortalHsSchema, type WoodsPortalHsConfig } from '../schema/woodsportal-hs.js'\nimport { WpHsError, ExitCode } from './errors.js'\nimport { findProjectRoot } from './paths.js'\n\nexport type LoadedProjectConfig = {\n root: string\n config: WoodsPortalHsConfig\n configPath: string\n configFileName: string\n}\n\nexport function loadConfig(projectRoot?: string): LoadedProjectConfig {\n const root = projectRoot ?? findProjectRoot()\n const resolved = resolveProjectConfig(root)\n\n if (!resolved) {\n throw new WpHsError(`Missing ${WOODSCLI_CONFIG_FILE}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp init or wp adopt. Legacy woodsportal.hs.json → run wp migrate --yes.',\n })\n }\n\n let raw: unknown\n try {\n raw = JSON.parse(readFileSync(resolved.path, 'utf8'))\n } catch (cause) {\n throw new WpHsError(`Invalid JSON in ${resolved.fileName}`, {\n exitCode: ExitCode.USER_ERROR,\n cause,\n })\n }\n\n const parsed = WoodsPortalHsSchema.safeParse(raw)\n if (!parsed.success) {\n throw new WpHsError(`${resolved.fileName} validation failed: ${parsed.error.message}`, {\n exitCode: ExitCode.USER_ERROR,\n remediation: 'Run wp config validate for details.',\n })\n }\n\n return {\n root,\n config: parsed.data,\n configPath: resolved.path,\n configFileName: resolved.fileName,\n }\n}\n\nexport function validateConfigFile(projectRoot?: string): {\n valid: boolean\n errors: string[]\n config?: WoodsPortalHsConfig\n} {\n try {\n const { config } = loadConfig(projectRoot)\n return { valid: true, errors: [], config }\n } catch (error) {\n if (error instanceof WpHsError) {\n return { valid: false, errors: [error.message] }\n }\n return { valid: false, errors: [String(error)] }\n }\n}\n","/** Must match dev-module-config/schema/types.ts */\nexport const MODULE_CONFIG_CHANGED = 'woodsportal:module-config-changed'\n\nexport type ModuleConfigBuiltState = {\n built: Record<string, unknown>\n}\n\n/** WoodsCLI consumer hook — mount app + sync module-config live data. */\nexport interface HubSpotKitAdapter {\n syncLiveData(built: Record<string, unknown>): void\n getDefaultHubSpotData?(): Record<string, unknown>\n mountDevApp?(root: unknown): void | Promise<void>\n}\n\nexport function bindAdapterToModuleConfig(adapter: HubSpotKitAdapter): void {\n if (typeof globalThis.window === 'undefined') return\n globalThis.window.addEventListener(MODULE_CONFIG_CHANGED, (event: Event) => {\n const detail = (event as CustomEvent<ModuleConfigBuiltState>).detail\n adapter.syncLiveData(detail.built)\n })\n}\n\nexport function syncAdapterFromModuleState(\n adapter: HubSpotKitAdapter,\n state: ModuleConfigBuiltState,\n): void {\n adapter.syncLiveData(state.built)\n}\n\n/** Minimal adapter for greenfield templates without a full app. */\nexport const defaultHubSpotKitAdapter: HubSpotKitAdapter = {\n syncLiveData(built) {\n const w = globalThis as typeof globalThis & { __WPHS_DATA__?: Record<string, unknown> }\n w.__WPHS_DATA__ = built\n },\n getDefaultHubSpotData() {\n return {}\n },\n}\n","/**\n * Generic fields.json → flat runtime object (WoodsCLI default transform).\n */\nexport type GenericFieldDefinition = {\n name: string\n type: string\n default?: unknown\n children?: GenericFieldDefinition[]\n}\n\nexport type GenericFieldValues = Record<string, unknown>\n\nfunction flattenField(\n field: GenericFieldDefinition,\n values: GenericFieldValues,\n out: Record<string, unknown>,\n): void {\n const key = field.name\n const value = values[key]\n\n if (field.type === 'group' && field.children && value && typeof value === 'object') {\n const groupValues = value as GenericFieldValues\n for (const child of field.children) {\n flattenField(child, groupValues, out)\n }\n return\n }\n\n if (field.type === 'repeater' && Array.isArray(value)) {\n out[key] = value\n return\n }\n\n out[key] = value ?? field.default ?? null\n}\n\nexport function mapFieldsToRuntime(\n schema: GenericFieldDefinition[],\n values: GenericFieldValues,\n): Record<string, unknown> {\n const out: Record<string, unknown> = {}\n for (const field of schema) {\n flattenField(field, values, out)\n }\n return out\n}\n"],"mappings":"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","names":[]}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
type VitePreset = 'dev' | 'hubspot' | 'cdn-vendor' | 'cdn-app' | 'cdn-esm';
|
|
2
|
+
declare function getViteConfigPath(preset: VitePreset): string;
|
|
3
|
+
|
|
4
|
+
type CreateViteConfigOptions = {
|
|
5
|
+
/** Vite preset shipped with @woodsportal/hubspot-kit */
|
|
6
|
+
preset: VitePreset;
|
|
7
|
+
/** Consumer project root (sets WP_HS_PROJECT_ROOT for kit scripts) */
|
|
8
|
+
projectRoot?: string;
|
|
9
|
+
};
|
|
10
|
+
/**
|
|
11
|
+
* Returns the absolute path to a kit Vite config preset.
|
|
12
|
+
* Consumer repos typically re-export: `export { default } from '@woodsportal/hubspot-kit/vite/vite.config.hubspot.mjs'`
|
|
13
|
+
* or use this helper in a thin wrapper config.
|
|
14
|
+
*/
|
|
15
|
+
declare function createViteConfig(presetOrOptions: VitePreset | CreateViteConfigOptions): string;
|
|
16
|
+
|
|
17
|
+
export { type CreateViteConfigOptions, type VitePreset, createViteConfig, getViteConfigPath };
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":[],"sourcesContent":[],"mappings":"","names":[]}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# WoodsCLI examples
|
|
2
|
+
|
|
3
|
+
Runnable catalog of module/theme setups and CLI use cases. See [docs/EXAMPLES.md](../docs/EXAMPLES.md).
|
|
4
|
+
|
|
5
|
+
```bash
|
|
6
|
+
npm run test:examples # static smoke (CI)
|
|
7
|
+
wp examples list # print manifest
|
|
8
|
+
wp examples smoke --tier static
|
|
9
|
+
wp examples smoke --scenario existing-module-hybrid
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
Fixtures under `fixtures/` simulate existing, adopted, and greenfield layouts. Greenfield live smokes require full templates (Phase 2).
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"name":"title","label":"Title","type":"text","default":"Hello"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div>{{ module.title }}</div>
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"configVersion": 1,
|
|
3
|
+
"projectType": "module-hybrid-cdn",
|
|
4
|
+
"module": {"name": "demo", "uploadPath": "dw-modules/demo.module", "outDir": "dist/Demo.Module"},
|
|
5
|
+
"cdn": {"mode": "jsdelivr", "buildIdFile": ".portal-build-id", "mirror": "portal.cdn.json"},
|
|
6
|
+
"dev": {"moduleConfigDir": ".wphs", "adapter": "./wphs.adapter.ts", "fieldsJson": "public/fields.json"}
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"name":"title","label":"Title","type":"text","default":"Hello"}]
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"configVersion": 1,
|
|
3
|
+
"projectType": "module-monolith",
|
|
4
|
+
"module": {"name": "demo", "uploadPath": "dw-modules/demo.module", "outDir": "dist/Demo.Module"},
|
|
5
|
+
"cdn": {"mode": "none", "buildIdFile": ".portal-build-id", "mirror": "portal.cdn.json"},
|
|
6
|
+
"dev": {"moduleConfigDir": ".wphs", "fieldsJson": "public/fields.json"}
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<html><body>{% block body %}{% endblock %}</body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"label":"Minimal","preview_path":"templates/page.html"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"configVersion": 1,
|
|
3
|
+
"projectType": "theme-starter",
|
|
4
|
+
"module": {"name": "n/a", "uploadPath": "n/a", "outDir": "dist/n/a"},
|
|
5
|
+
"theme": {"name": "minimal", "uploadPath": "themes/minimal", "srcDir": "src/theme", "themeJson": "src/theme/theme.json"},
|
|
6
|
+
"dev": {"moduleConfigDir": ".wphs", "fieldsJson": "public/fields.json"}
|
|
7
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"name":"title","label":"Title","type":"text","default":"Hello"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div>{{ module.title }}</div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<html><body>{% block body %}{% endblock %}</body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"label":"Minimal","preview_path":"templates/page.html"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html><head><meta charset="UTF-8"/><title>WoodsCLI dev</title></head><body><div id="app"></div><script type="module" src="/src/main.tsx"></script></body></html>
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "my-hubspot-portal-module",
|
|
3
|
+
"private": true,
|
|
4
|
+
"type": "module",
|
|
5
|
+
"scripts": {
|
|
6
|
+
"local": "wp dev",
|
|
7
|
+
"doctor": "wp doctor",
|
|
8
|
+
"build:prod": "wp build --tier prod",
|
|
9
|
+
"deploy:prod": "wp deploy --tier prod"
|
|
10
|
+
},
|
|
11
|
+
"devDependencies": {
|
|
12
|
+
"@woodsportal/hubspot-kit": "^1.0.0"
|
|
13
|
+
}
|
|
14
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"name":"title","label":"Title","type":"text","default":"WoodsCLI module"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<div id="app"></div>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.dev.mjs'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.hubspot.mjs'
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
{
|
|
2
|
+
"configVersion": 1,
|
|
3
|
+
"projectType": "module-hybrid-cdn",
|
|
4
|
+
"module": {"name": "my-portal-module", "uploadPath": "dw-modules/my-portal-module.module", "outDir": "dist/MyPortal.Module"},
|
|
5
|
+
"cdn": {"mode": "jsdelivr", "buildIdFile": ".portal-build-id", "mirror": "portal.cdn.json"},
|
|
6
|
+
"entries": {"app": "src/cdn/app-entry.ts", "vendor": "src/cdn/vendor-entry.ts", "hubspotBootstrap": "src/hubspot/bootstrap.ts", "localDev": "src/main.tsx"},
|
|
7
|
+
"dev": {"moduleConfigDir": ".wphs", "adapter": "./wphs.adapter.ts", "fieldsJson": "public/fields.json"}
|
|
8
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
[{"name":"title","label":"Title","type":"text","default":"WoodsCLI module"}]
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.dev.mjs'
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.monolith.mjs'
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
{
|
|
2
|
+
"configVersion": 1,
|
|
3
|
+
"projectType": "module-monolith",
|
|
4
|
+
"module": {
|
|
5
|
+
"name": "my-portal-module",
|
|
6
|
+
"uploadPath": "dw-modules/my-portal-module.module",
|
|
7
|
+
"outDir": "dist/MyPortal.Module"
|
|
8
|
+
},
|
|
9
|
+
"cdn": {
|
|
10
|
+
"mode": "none",
|
|
11
|
+
"buildIdFile": ".portal-build-id",
|
|
12
|
+
"mirror": "portal.cdn.json"
|
|
13
|
+
},
|
|
14
|
+
"entries": {
|
|
15
|
+
"app": "src/cdn/app-entry.ts",
|
|
16
|
+
"vendor": "src/cdn/vendor-entry.ts",
|
|
17
|
+
"hubspotBootstrap": "src/hubspot/bootstrap.ts",
|
|
18
|
+
"localDev": "src/main.tsx"
|
|
19
|
+
},
|
|
20
|
+
"dev": {
|
|
21
|
+
"moduleConfigDir": ".wphs",
|
|
22
|
+
"adapter": "./wphs.adapter.ts",
|
|
23
|
+
"fieldsJson": "public/fields.json"
|
|
24
|
+
}
|
|
25
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
body { font-family: system-ui; }
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
<!doctype html><html><head><link rel="stylesheet" href="../css/main.css"/></head><body>{% block body %}{% endblock %}</body></html>
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{% extends "./base.html" %}{% block body %}<h1>WoodsCLI theme</h1>{% endblock %}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"label":"My Theme","preview_path":"templates/page.html"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { default } from '@woodsportal/hubspot-kit/vite/vite.config.dev.mjs'
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
{
|
|
2
|
+
"configVersion": 1,
|
|
3
|
+
"projectType": "theme-starter",
|
|
4
|
+
"module": {"name": "n/a", "uploadPath": "n/a", "outDir": "dist/n/a"},
|
|
5
|
+
"theme": {"name": "my-theme", "uploadPath": "themes/my-theme", "srcDir": "src/theme", "themeJson": "src/theme/theme.json"},
|
|
6
|
+
"dev": {"moduleConfigDir": ".wphs", "fieldsJson": "public/fields.json"}
|
|
7
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
{
|
|
2
|
+
"version": 1,
|
|
3
|
+
"scenarios": [
|
|
4
|
+
{ "id": "greenfield-module-hybrid-cdn", "setup": "greenfield", "track": "module", "projectType": "module-hybrid-cdn", "smokeTier": "live", "fixture": "greenfield/module-hybrid-cdn", "checks": ["layout"] },
|
|
5
|
+
{ "id": "greenfield-module-monolith", "setup": "greenfield", "track": "module", "projectType": "module-monolith", "smokeTier": "live", "fixture": "greenfield/module-monolith", "checks": ["layout"] },
|
|
6
|
+
{ "id": "greenfield-theme-starter", "setup": "greenfield", "track": "theme", "projectType": "theme-starter", "smokeTier": "live", "fixture": "greenfield/theme-starter", "checks": ["layout"] },
|
|
7
|
+
{ "id": "existing-module-hybrid", "setup": "existing", "track": "module", "smokeTier": "static", "fixture": "fixtures/existing-module-hybrid" },
|
|
8
|
+
{ "id": "existing-theme-minimal", "setup": "existing", "track": "theme", "smokeTier": "static", "fixture": "fixtures/existing-theme-minimal" },
|
|
9
|
+
{ "id": "adopt-module-hybrid", "setup": "adopted", "track": "module", "projectType": "module-hybrid-cdn", "smokeTier": "static", "fixture": "fixtures/adopted-module-hybrid" },
|
|
10
|
+
{ "id": "adopt-theme-minimal", "setup": "adopted", "track": "theme", "projectType": "theme-starter", "smokeTier": "static", "fixture": "fixtures/adopted-theme-minimal" },
|
|
11
|
+
{ "id": "cdn-mode-jsdelivr", "setup": "adopted", "track": "module", "projectType": "module-hybrid-cdn", "smokeTier": "static", "fixture": "fixtures/adopted-module-hybrid", "checks": ["cdn.mode=jsdelivr"] },
|
|
12
|
+
{ "id": "cdn-mode-none-monolith", "setup": "adopted", "track": "module", "projectType": "module-monolith", "smokeTier": "static", "fixture": "fixtures/adopted-module-monolith" },
|
|
13
|
+
{ "id": "build-split-cdn-only", "setup": "adopted", "track": "module", "smokeTier": "static", "fixture": "fixtures/adopted-module-hybrid", "checks": ["build.cdn-only.dry-run"] },
|
|
14
|
+
{ "id": "build-split-hubspot-only", "setup": "adopted", "track": "module", "smokeTier": "static", "fixture": "fixtures/adopted-module-hybrid", "checks": ["build.hubspot-only.dry-run"] },
|
|
15
|
+
{ "id": "audit-ship-bundle", "setup": "adopted", "track": "module", "smokeTier": "static", "checks": ["audit.ship-bundle.doc"] },
|
|
16
|
+
{ "id": "audit-parity-baseline", "setup": "adopted", "track": "module", "smokeTier": "static", "checks": ["audit.baseline.path"] },
|
|
17
|
+
{ "id": "migrate-dev-to-wphs", "setup": "adopted", "track": "module", "smokeTier": "static", "checks": ["migrate.doc"] },
|
|
18
|
+
{ "id": "module-config-overlay", "setup": "adopted", "track": "module", "smokeTier": "live", "fixture": "fixtures/adopted-module-hybrid", "checks": ["wphs.config.dir"] }
|
|
19
|
+
]
|
|
20
|
+
}
|
|
@@ -0,0 +1,100 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* Run examples smoke matrix (static tier default — no HubSpot account required).
|
|
4
|
+
*/
|
|
5
|
+
import { existsSync, mkdirSync, readFileSync } from 'node:fs'
|
|
6
|
+
import { dirname, join, resolve } from 'node:path'
|
|
7
|
+
import { fileURLToPath } from 'node:url'
|
|
8
|
+
|
|
9
|
+
import { detectProjectLayout } from '../../scripts/detect-layout.mjs'
|
|
10
|
+
|
|
11
|
+
const __dirname = dirname(fileURLToPath(import.meta.url))
|
|
12
|
+
const examplesRoot = resolve(__dirname, '..')
|
|
13
|
+
const kitRoot = resolve(examplesRoot, '..')
|
|
14
|
+
|
|
15
|
+
const tierArg = process.argv.indexOf('--tier')
|
|
16
|
+
const tier = tierArg >= 0 ? process.argv[tierArg + 1] : 'static'
|
|
17
|
+
const scenarioArg = process.argv.indexOf('--scenario')
|
|
18
|
+
const scenarioFilter = scenarioArg >= 0 ? process.argv[scenarioArg + 1] : null
|
|
19
|
+
|
|
20
|
+
const manifest = JSON.parse(readFileSync(join(examplesRoot, 'manifest.json'), 'utf8'))
|
|
21
|
+
|
|
22
|
+
/** @param {import('../../scripts/detect-layout.mjs').detectProjectLayout extends Function ? ReturnType<import('../../scripts/detect-layout.mjs').detectProjectLayout> : never} layout */
|
|
23
|
+
function assertLayout(layout, expectedTrack) {
|
|
24
|
+
if (layout.track !== expectedTrack) {
|
|
25
|
+
throw new Error(`Expected track ${expectedTrack}, got ${layout.track}`)
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
/** @param {string} fixtureRel */
|
|
30
|
+
function fixtureRoot(fixtureRel) {
|
|
31
|
+
return resolve(examplesRoot, fixtureRel)
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/** @param {{ id: string; track?: string; fixture?: string; checks?: string[]; projectType?: string }} scenario */
|
|
35
|
+
function runStaticScenario(scenario) {
|
|
36
|
+
const checks = scenario.checks ?? ['layout', 'wphs.tree']
|
|
37
|
+
|
|
38
|
+
if (checks.includes('layout') && scenario.fixture) {
|
|
39
|
+
const root = fixtureRoot(scenario.fixture)
|
|
40
|
+
if (!existsSync(root)) {
|
|
41
|
+
throw new Error(`Missing fixture ${scenario.fixture}`)
|
|
42
|
+
}
|
|
43
|
+
const layout = detectProjectLayout(root)
|
|
44
|
+
if (scenario.track && layout.track !== scenario.track) {
|
|
45
|
+
throw new Error(`${scenario.id}: track mismatch`)
|
|
46
|
+
}
|
|
47
|
+
if (scenario.projectType && layout.projectType !== scenario.projectType) {
|
|
48
|
+
throw new Error(`${scenario.id}: projectType mismatch`)
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
if (checks.includes('wphs.tree') || checks.includes('wphs.config.dir')) {
|
|
53
|
+
const root = scenario.fixture ? fixtureRoot(scenario.fixture) : kitRoot
|
|
54
|
+
const layout = detectProjectLayout(root)
|
|
55
|
+
if (layout.hasWphs || existsSync(join(root, '.wphs', 'config'))) {
|
|
56
|
+
// ok
|
|
57
|
+
} else if (scenario.setup === 'existing') {
|
|
58
|
+
// existing fixtures may lack .wphs until adopt
|
|
59
|
+
} else if (scenario.setup === 'adopted') {
|
|
60
|
+
throw new Error(`${scenario.id}: expected .wphs workspace`)
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
if (checks.includes('audit.baseline.path')) {
|
|
65
|
+
const baseline = join(examplesRoot, 'fixtures', 'adopted-module-hybrid', '.wphs', 'audit', 'baseline')
|
|
66
|
+
if (!existsSync(baseline)) {
|
|
67
|
+
mkdirSync(baseline, { recursive: true })
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
return { id: scenario.id, ok: true }
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const results = []
|
|
75
|
+
let scenarios = manifest.scenarios
|
|
76
|
+
if (scenarioFilter) scenarios = scenarios.filter((s) => s.id === scenarioFilter)
|
|
77
|
+
|
|
78
|
+
for (const scenario of scenarios) {
|
|
79
|
+
if (tier === 'live' && scenario.smokeTier !== 'live') {
|
|
80
|
+
results.push({ id: scenario.id, ok: true, skipped: 'not live tier' })
|
|
81
|
+
continue
|
|
82
|
+
}
|
|
83
|
+
if (tier === 'static') {
|
|
84
|
+
try {
|
|
85
|
+
runStaticScenario(scenario)
|
|
86
|
+
results.push({ id: scenario.id, ok: true })
|
|
87
|
+
console.log(`[examples:smoke] OK ${scenario.id}`)
|
|
88
|
+
} catch (error) {
|
|
89
|
+
results.push({ id: scenario.id, ok: false, error: error instanceof Error ? error.message : String(error) })
|
|
90
|
+
console.error(`[examples:smoke] FAIL ${scenario.id}:`, error)
|
|
91
|
+
}
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
const failed = results.filter((r) => r.ok === false)
|
|
96
|
+
if (failed.length > 0) {
|
|
97
|
+
process.exit(1)
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
console.log(`[examples:smoke] ${results.length} scenario(s) passed (${tier})`)
|