Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,13 @@ jobs:
working-directory: packages/b2c-cli
run: pnpm run pretest && pnpm run test:ci && pnpm run lint

- name: Run VS Extension lint
id: vs-extension-test
if: always() && steps.vs-extension-test.conclusion != 'cancelled'
working-directory: packages/b2c-vs-extension
# Testing not currently supported on CI
run: pnpm run lint

- name: Test Report
uses: dorny/test-reporter@fe45e9537387dac839af0d33ba56eed8e24189e8 # v2.3.0
if: always() && steps.sdk-test.conclusion != 'cancelled'
Expand Down
4 changes: 2 additions & 2 deletions packages/b2c-vs-extension/.vscode-test.mjs
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { defineConfig } from '@vscode/test-cli';
import {defineConfig} from '@vscode/test-cli';

export default defineConfig({
files: 'out/test/**/*.test.js',
files: 'out/test/**/*.test.js',
});
16 changes: 9 additions & 7 deletions packages/b2c-vs-extension/eslint.config.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import {includeIgnoreFile} from '@eslint/compat';
import headerPlugin from 'eslint-plugin-header';
import path from 'node:path';
import {fileURLToPath} from 'node:url';
import typescriptEslint from 'typescript-eslint';
import tseslint from 'typescript-eslint';

import {copyrightHeader, sharedRules, prettierPlugin} from '../../eslint.config.mjs';

Expand All @@ -16,17 +16,19 @@ headerPlugin.rules.header.meta.schema = false;

export default [
includeIgnoreFile(gitignorePath),
...typescriptEslint.config({
files: ['**/*.ts'],
languageOptions: {
parserOptions: {ecmaVersion: 2022, sourceType: 'module'},
},
}),
{
ignores: ['src/template/**'],
},
...tseslint.configs.recommended,
prettierPlugin,
{
files: ['**/*.ts'],
plugins: {
header: headerPlugin,
},
languageOptions: {
parserOptions: {ecmaVersion: 2022, sourceType: 'module'},
},
rules: {
'header/header': ['error', 'block', copyrightHeader],
...sharedRules,
Expand Down
1 change: 1 addition & 0 deletions packages/b2c-vs-extension/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@
"format": "prettier --write src",
"format:check": "prettier --check src",
"test": "vscode-test",
"posttest": "pnpm run lint",
"analyze": "ANALYZE_BUNDLE=1 node scripts/esbuild-bundle.mjs"
},
"devDependencies": {
Expand Down
84 changes: 41 additions & 43 deletions packages/b2c-vs-extension/scripts/esbuild-bundle.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@
* Bundles the extension with esbuild. Injects a shim for import.meta.url so
* SDK code that uses createRequire(import.meta.url) works in CJS output.
*/
import esbuild from "esbuild";
import fs from "node:fs";
import path from "path";
import { fileURLToPath } from "url";
import esbuild from 'esbuild';
import fs from 'node:fs';
import path from 'path';
import {fileURLToPath} from 'url';

const __filename = fileURLToPath(import.meta.url);
const __dirname = path.dirname(__filename);

// scripts/ -> package root
const pkgRoot = path.resolve(__dirname, "..");
const pkgRoot = path.resolve(__dirname, '..');

// In CJS there is no import.meta; SDK's version.js uses createRequire(import.meta.url). Shim it.
// Use globalThis so the value is visible inside all module wrappers in the bundle.
Expand All @@ -25,19 +25,19 @@ const IMPORT_META_URL_SHIM =

function loaderFor(filePath) {
const ext = path.extname(filePath);
if (ext === ".ts" || filePath.endsWith(".tsx")) return "ts";
return "js";
if (ext === '.ts' || filePath.endsWith('.tsx')) return 'ts';
return 'js';
}

const importMetaUrlPlugin = {
name: "import-meta-url-shim",
name: 'import-meta-url-shim',
setup(build) {
build.onLoad({ filter: /\.(ts|tsx|js|mjs|cjs)$/ }, (args) => {
const contents = fs.readFileSync(args.path, "utf-8");
const replaced = contents.includes("import.meta.url")
? contents.replace(/import\.meta\.url/g, "globalThis.__import_meta_url")
build.onLoad({filter: /\.(ts|tsx|js|mjs|cjs)$/}, (args) => {
const contents = fs.readFileSync(args.path, 'utf-8');
const replaced = contents.includes('import.meta.url')
? contents.replace(/import\.meta\.url/g, 'globalThis.__import_meta_url')
: contents;
return { contents: replaced, loader: loaderFor(args.path) };
return {contents: replaced, loader: loaderFor(args.path)};
});
},
};
Expand All @@ -47,69 +47,67 @@ const importMetaUrlPlugin = {
// we replace that require in the bundle output with the actual JSON (post-build).
// Also replace require.resolve('@salesforce/b2c-tooling-sdk/package.json') so it doesn't throw when
// the extension runs from a VSIX (no node_modules). We use __dirname so path.dirname(...) is the extension dist.
const sdkPkgJsonPath = path.join(pkgRoot, "..", "b2c-tooling-sdk", "package.json");
const REQUIRE_RESOLVE_PACKAGE_JSON_RE = /require\d*\.resolve\s*\(\s*["']@salesforce\/b2c-tooling-sdk\/package\.json["']\s*\)/g;
const sdkPkgJsonPath = path.join(pkgRoot, '..', 'b2c-tooling-sdk', 'package.json');
const REQUIRE_RESOLVE_PACKAGE_JSON_RE =
/require\d*\.resolve\s*\(\s*["']@salesforce\/b2c-tooling-sdk\/package\.json["']\s*\)/g;
const REQUIRE_RESOLVE_REPLACEMENT = "require('path').join(__dirname, 'package.json')";

function inlineSdkPackageJson() {
const outPath = path.join(pkgRoot, "dist", "extension.js");
let str = fs.readFileSync(outPath, "utf8");
const sdkPkg = JSON.stringify(JSON.parse(fs.readFileSync(sdkPkgJsonPath, "utf8")));
str = str.replace(
/require\d*\s*\(\s*["']@salesforce\/b2c-tooling-sdk\/package\.json["']\s*\)/g,
sdkPkg
);
const outPath = path.join(pkgRoot, 'dist', 'extension.js');
let str = fs.readFileSync(outPath, 'utf8');
const sdkPkg = JSON.stringify(JSON.parse(fs.readFileSync(sdkPkgJsonPath, 'utf8')));
str = str.replace(/require\d*\s*\(\s*["']@salesforce\/b2c-tooling-sdk\/package\.json["']\s*\)/g, sdkPkg);
str = str.replace(REQUIRE_RESOLVE_PACKAGE_JSON_RE, REQUIRE_RESOLVE_REPLACEMENT);
fs.writeFileSync(outPath, str, "utf8");
fs.writeFileSync(outPath, str, 'utf8');
}

const watchMode = process.argv.includes("--watch");
const watchMode = process.argv.includes('--watch');

const buildOptions = {
entryPoints: [path.join(pkgRoot, "src", "extension.ts")],
entryPoints: [path.join(pkgRoot, 'src', 'extension.ts')],
bundle: true,
platform: "node",
format: "cjs",
target: "node18",
outfile: path.join(pkgRoot, "dist", "extension.js"),
platform: 'node',
format: 'cjs',
target: 'node18',
outfile: path.join(pkgRoot, 'dist', 'extension.js'),
sourcemap: true,
metafile: true,
external: ["vscode"],
external: ['vscode'],
// In watch mode, include "development" so esbuild resolves the SDK's exports to .ts source files
// directly (no SDK rebuild needed). Production builds use the built dist/ artifacts.
conditions: watchMode
? ["development", "require", "node", "default"]
: ["require", "node", "default"],
mainFields: ["main", "module"],
banner: { js: IMPORT_META_URL_SHIM },
conditions: watchMode ? ['development', 'require', 'node', 'default'] : ['require', 'node', 'default'],
mainFields: ['main', 'module'],
banner: {js: IMPORT_META_URL_SHIM},
plugins: [importMetaUrlPlugin],
logLevel: "info",
logLevel: 'info',
};

if (watchMode) {
const ctx = await esbuild.context(buildOptions);
await ctx.watch();
console.log("[esbuild] watching for changes...");
console.log('[esbuild] watching for changes...');
} else {
const result = await esbuild.build(buildOptions);

inlineSdkPackageJson();

if (result.metafile && process.env.ANALYZE_BUNDLE) {
const metaPath = path.join(pkgRoot, "dist", "meta.json");
fs.writeFileSync(metaPath, JSON.stringify(result.metafile, null, 2), "utf-8");
const metaPath = path.join(pkgRoot, 'dist', 'meta.json');
fs.writeFileSync(metaPath, JSON.stringify(result.metafile, null, 2), 'utf-8');
const inputs = Object.entries(result.metafile.inputs).map(([file, info]) => ({
file: path.relative(pkgRoot, file),
bytes: info.bytes,
}));
inputs.sort((a, b) => b.bytes - a.bytes);
const total = inputs.reduce((s, i) => s + i.bytes, 0);
console.log("\n--- Bundle analysis (top 40 by input size) ---");
console.log('\n--- Bundle analysis (top 40 by input size) ---');
console.log(`Total inputs: ${(total / 1024 / 1024).toFixed(2)} MB\n`);
inputs.slice(0, 40).forEach(({ file, bytes }, i) => {
inputs.slice(0, 40).forEach(({file, bytes}, i) => {
const pct = ((bytes / total) * 100).toFixed(1);
console.log(`${String(i + 1).padStart(2)} ${(bytes / 1024).toFixed(1).padStart(8)} KB ${pct.padStart(5)}% ${file}`);
console.log(
`${String(i + 1).padStart(2)} ${(bytes / 1024).toFixed(1).padStart(8)} KB ${pct.padStart(5)}% ${file}`,
);
});
console.log("\nWrote", metaPath);
console.log('\nWrote', metaPath);
}
}
4 changes: 2 additions & 2 deletions packages/b2c-vs-extension/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -527,7 +527,7 @@ function activateInner(context: vscode.ExtensionContext, log: vscode.OutputChann
return folder;
}

function resolveCliScript(context: vscode.ExtensionContext): {node: string; script: string} | null {
function _resolveCliScript(context: vscode.ExtensionContext): {node: string; script: string} | null {
const workspaceRoot = vscode.workspace.workspaceFolders?.[0]?.uri.fsPath;
if (workspaceRoot) {
const distCli = path.join(workspaceRoot, 'dist', 'cli.js');
Expand Down Expand Up @@ -1040,7 +1040,7 @@ function activateInner(context: vscode.ExtensionContext, log: vscode.OutputChann
'sfcc.shopper-products',
'sfcc.shopper-stores',
];
const {data, error, response} = await slasClient.PUT('/tenants/{tenantId}/clients/{clientId}', {
const {error, response} = await slasClient.PUT('/tenants/{tenantId}/clients/{clientId}', {
params: {path: {tenantId, clientId}},
body: {
clientId,
Expand Down
Loading