diff --git a/package-lock.json b/package-lock.json index a78878f949..43d835ea65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -11,7 +11,8 @@ "dependencies": { "@babel/parser": "^7.27.5", "@babel/traverse": "^7.27.4", - "node-fetch": "^2.6.9" + "node-fetch": "^2.6.9", + "prettier": "^3.6.2" }, "devDependencies": { "@playwright/test": "^1.49.0", @@ -7936,6 +7937,20 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.6.2.tgz", + "integrity": "sha512-I7AIg5boAr5R0FFtJ6rCfD+LFsWHp81dolrFD8S79U9tb8Az2nGrJncnMSnys+bpQJfRUzqs9hnA81OAA3hCuQ==", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-format": { "version": "29.4.3", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.4.3.tgz", diff --git a/package.json b/package.json index 0d4094dafb..07a3573732 100644 --- a/package.json +++ b/package.json @@ -41,6 +41,7 @@ "dependencies": { "@babel/parser": "^7.27.5", "@babel/traverse": "^7.27.4", - "node-fetch": "^2.6.9" + "node-fetch": "^2.6.9", + "prettier": "^3.6.2" } } diff --git a/packages/pwa-kit-create-app/CHANGELOG.md b/packages/pwa-kit-create-app/CHANGELOG.md index b23ec2607b..de64d898f8 100644 --- a/packages/pwa-kit-create-app/CHANGELOG.md +++ b/packages/pwa-kit-create-app/CHANGELOG.md @@ -1,6 +1,7 @@ ## 4.0.0-extensibility-preview.5 - Deprecate V3 Extensibility and experimental V4 Extensibility (#2573) - Move extensibility logic to generator (#2573) +- Apply prettier to trimmed files (#2688) ## v3.10.0 (Feb 18, 2025) - Add Data Cloud API configuration to `default.js`. [#2318] (https://github.com/SalesforceCommerceCloud/pwa-kit/pull/2229) diff --git a/packages/pwa-kit-create-app/scripts/trim-extensions.js b/packages/pwa-kit-create-app/scripts/trim-extensions.js index 8d1187dbb9..131e6f0e41 100644 --- a/packages/pwa-kit-create-app/scripts/trim-extensions.js +++ b/packages/pwa-kit-create-app/scripts/trim-extensions.js @@ -11,6 +11,7 @@ const traverse = require('@babel/traverse').default const generate = require('@babel/generator').default const path = require('path') const pluginConfig = require('../assets/plugin-config') +const {execSync} = require('child_process') const removeComponentCandidates = [] // List of files that are candidates for removal, as a result of trimming. const SEPARATOR = path.sep // Use OS-specific path separator @@ -218,6 +219,8 @@ function processFile(filePath, plugins) { }).code // Replace the original file with the trimmed version fs.writeFileSync(filePath, output) + // prettify the file + execSync(`npx prettier --write ${filePath}`) console.log(`Updated file ${filePath}`) } catch (e) { console.error(`Error updating file ${filePath}: ${e.message}`) diff --git a/packages/pwa-kit-create-app/scripts/trim-extensions.test.js b/packages/pwa-kit-create-app/scripts/trim-extensions.test.js index 47724e2612..061b77e06c 100644 --- a/packages/pwa-kit-create-app/scripts/trim-extensions.test.js +++ b/packages/pwa-kit-create-app/scripts/trim-extensions.test.js @@ -6,6 +6,8 @@ */ /* eslint-disable @typescript-eslint/no-var-requires */ const fs = require('fs') +const {execSync} = require('child_process') +const path = require('path') jest.mock('../assets/plugin-config', () => ({ plugins: { @@ -19,6 +21,41 @@ jest.mock('../assets/plugin-config', () => ({ })) jest.mock('fs') +jest.mock('child_process') + +// custom matcher to compare strings line by line with trimming +expect.extend({ + toEqualTrimmedLines(received, expected) { + const clean = (str) => + str + .split('\n') + .map((line) => line.trim()) // Trim each line + .filter((line) => line.length > 0) // Optional: remove empty lines + + const receivedLines = clean(received) + const expectedLines = clean(expected) + + const pass = this.equals(receivedLines, expectedLines) + + if (pass) { + return { + pass: true, + message: () => + `✅ Expected strings not to match line by line (but they did).\n\nExpected: ${this.utils.printExpected( + expectedLines + )}\nReceived: ${this.utils.printReceived(receivedLines)}` + } + } else { + return { + pass: false, + message: () => + `❌ Expected strings to match line by line (with trimming).\n\nExpected: ${this.utils.printExpected( + expectedLines + )}\nReceived: ${this.utils.printReceived(receivedLines)}` + } + } + } +}) const trimExtensions = require('./trim-extensions') @@ -47,6 +84,7 @@ describe('trim-extensions', () => { } }) fs.unlinkSync.mockReturnValue(true) + execSync.mockReturnValue(true) }) it('handles OR operator correctly', () => { @@ -62,7 +100,16 @@ describe('trim-extensions', () => { trimExtensions('/mock/dir', {SFDC_EXT_featureA: true, SFDC_EXT_featureB: false}) expect(fs.writeFileSync).toHaveBeenCalledWith( expect.any(String), - "const feature = 'Feature Enabled';" + expect.toEqualTrimmedLines("const feature = 'Feature Enabled';") + ) + expect(execSync).toHaveBeenCalledWith( + `npx prettier --write ${path.join( + '/mock', + 'dir', + 'src', + 'components', + 'featureComponent.jsx' + )}` ) }) @@ -82,12 +129,21 @@ describe('trim-extensions', () => { expect(fs.writeFileSync).toHaveBeenCalledWith( expect.any(String), - "const featureAFunc = () => 'Feature A';" + expect.toEqualTrimmedLines("const featureAFunc = () => 'Feature A';") ) expect(fs.writeFileSync).toHaveBeenCalledWith( expect.any(String), expect.not.stringContaining("const featureBFunc = () => 'Feature B';") ) + expect(execSync).toHaveBeenCalledWith( + `npx prettier --write ${path.join( + '/mock', + 'dir', + 'src', + 'components', + 'featureComponent.jsx' + )}` + ) }) it('handles variable with ternary expressions correctly', () => { @@ -104,12 +160,21 @@ describe('trim-extensions', () => { expect(fs.writeFileSync).toHaveBeenCalledWith( expect.any(String), - 'const showFeature = Feature_A;' + expect.toEqualTrimmedLines('const showFeature = Feature_A;') ) expect(fs.writeFileSync).toHaveBeenCalledWith( expect.any(String), expect.not.stringContaining('const showFeature = Feature_B') ) + expect(execSync).toHaveBeenCalledWith( + `npx prettier --write ${path.join( + '/mock', + 'dir', + 'src', + 'components', + 'featureComponent.jsx' + )}` + ) }) it('handles return with ternary expressions correctly', () => { @@ -122,9 +187,71 @@ describe('trim-extensions', () => { trimExtensions('/mock/dir', {SFDC_EXT_featureA: true}) + const expected = ` + function test() { + return Feature_A; + } + ` + expect(fs.writeFileSync).toHaveBeenCalledWith( + expect.any(String), + expect.toEqualTrimmedLines(expected) + ) + expect(execSync).toHaveBeenCalledWith( + `npx prettier --write ${path.join( + '/mock', + 'dir', + 'src', + 'components', + 'featureComponent.jsx' + )}` + ) + }) + + it('handles PropTypes declarations correctly', () => { + const code = ` + MyClass.PropTypes = { + name: PropTypes.string, + description: PropTypes.string + }; + SFDC_EXT_featureA && (MyClass.PropType = { + ...MyClass.PropType, + featureAProp: PropTypes.string + }); + SFDC_EXT_featureB && (MyClass.PropType = { + ...MyClass.PropType, + featureBProp: PropTypes.string + }); + ` + fs.readFileSync.mockReturnValue(code) + + trimExtensions('/mock/dir', {SFDC_EXT_featureA: true}) + + const expected = ` + MyClass.PropTypes = { + name: PropTypes.string, + description: PropTypes.string + }; + MyClass.PropType = { + ...MyClass.PropType, + featureAProp: PropTypes.string + }; + ` expect(fs.writeFileSync).toHaveBeenCalledWith( expect.any(String), - expect.stringContaining('return Feature_A') + expect.toEqualTrimmedLines(expected) + ) + expect(fs.writeFileSync).not.toHaveBeenCalledWith( + expect.any(String), + expect.stringContaining('featureBProp: PropTypes.string') + ) + expect(execSync).toHaveBeenCalledWith( + `npx prettier --write ${path.join( + '/mock', + 'dir', + 'src', + 'components', + 'featureComponent.jsx' + )}` ) }) @@ -143,14 +270,31 @@ describe('trim-extensions', () => { trimExtensions('/mock/dir', {SFDC_EXT_featureA: true, SFDC_EXT_featureB: false}) + const expected = ` + function test() { + return ( +