Skip to content

Instantly share code, notes, and snippets.

@kevinxh
Last active March 19, 2024 17:01
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save kevinxh/8c5dfcb7e988d9c321a242d824c75613 to your computer and use it in GitHub Desktop.
Save kevinxh/8c5dfcb7e988d9c321a242d824c75613 to your computer and use it in GitHub Desktop.
PWA Kit extensibility eject and restore CLI command

What is this tool?

This tool essentially help you "eject" files from the extended projects.

Usage

Eject - copy files from the base template into the overrides folder

pwa-kit-dev eject app/pages/home/index.jsx

Restore - remove all unchanged files from override folder

pwa-kit-dev restore

Install

npx @salesforce/pwa-kit-create-app@3.0.0-preview.0 --outputDir generated-project
cd generated-project
git init

# copy cli.patch to your local machine
git apply cli.patch
npm i
export PATH="$(pwd)/bin:$PATH"
diff --git a/bin/pwa-kit-dev b/bin/pwa-kit-dev
new file mode 100755
index 0000000..a4dd041
--- /dev/null
+++ b/bin/pwa-kit-dev
@@ -0,0 +1,16 @@
+#!/usr/bin/env node
+
+// const oclif = require('@oclif/core')
+
+// oclif.run().then(require('@oclif/core/flush')).catch(require('@oclif/core/handle'))
+
+const oclif = require('@oclif/core')
+const path = require('path')
+
+process.env.NODE_ENV = 'development'
+
+// In dev mode, always show stack traces
+oclif.settings.debug = true
+
+// Start the CLI
+oclif.run().then(oclif.flush).catch(oclif.Errors.handle)
diff --git a/commands/eject.js b/commands/eject.js
new file mode 100644
index 0000000..b66ec0b
--- /dev/null
+++ b/commands/eject.js
@@ -0,0 +1,41 @@
+const {Command, Args} = require('@oclif/core')
+const path = require('path')
+const fs = require('fs')
+
+class Eject extends Command {
+ async run() {
+ console.log('Running eject command...')
+ const {args} = await this.parse(Eject)
+
+ const srcPath = path.resolve('./node_modules/@salesforce/retail-react-app', args.file)
+ const destPath = path.resolve('./overrides', args.file)
+ // console.log(filePath)
+ ensureDirectoryExistence(destPath)
+
+ fs.copyFileSync(srcPath, destPath)
+ console.log(`File ${args.file} is ejected`)
+ }
+}
+
+function ensureDirectoryExistence(filePath) {
+ var dirname = path.dirname(filePath);
+ if (fs.existsSync(dirname)) {
+ return true;
+ }
+ ensureDirectoryExistence(dirname);
+ fs.mkdirSync(dirname);
+}
+
+Eject.description = 'Eject files from base template'
+Eject.args = {
+ file: Args.string(
+ {
+ name: 'file', // name of arg to show in help and reference with args[name]
+ required: true, // make the arg required with `required: true`
+ description: 'the file you want to eject from base template', // help description
+ hidden: false, // hide this arg from help
+ }
+ ),
+}
+
+module.exports = Eject
\ No newline at end of file
diff --git a/commands/restore.js b/commands/restore.js
new file mode 100644
index 0000000..aaef670
--- /dev/null
+++ b/commands/restore.js
@@ -0,0 +1,50 @@
+const {Command, Args} = require('@oclif/core')
+const path = require('path')
+const fs = require('fs')
+const {glob} = require('glob')
+const {equalFiles} = require('file-sync-cmp')
+
+class Restore extends Command {
+ async run() {
+ console.log('Running restore command...')
+ const filesArray = await glob('./overrides/**/*.{js,jsx}')
+ // console.log(filesArray)
+
+ if (!filesArray.length) {
+ console.log('No restorable files found, exit.')
+ return
+ }
+
+ const filteredFilesArray = filesArray.filter((file) => {
+ return !file.includes('ssr.js')
+ && !file.includes('request-processor.js')
+ && !file.includes('routes.jsx')
+ && !file.includes('main.jsx')
+ })
+
+ filteredFilesArray.forEach((file) => {
+ // console.log(file)
+
+ const strippedPath = file.split('overrides/')[1]
+ const baseTemplateFilePath = path.resolve('./node_modules/@salesforce/retail-react-app', strippedPath)
+
+ // base template doesn't have the file
+ if (!fs.existsSync(baseTemplateFilePath)) {
+ return
+ }
+
+ // console.log(strippedPath + ' exists in base template')
+ const overrideFilePath = path.resolve(file)
+
+ if (equalFiles(baseTemplateFilePath, overrideFilePath)) {
+ fs.unlinkSync(overrideFilePath)
+ console.log(strippedPath + ' is removed')
+ }
+ })
+
+ }
+}
+
+Restore.description = 'Restore files from base template'
+
+module.exports = Restore
diff --git a/package.json b/package.json
index 8aa2b0b..fe5e94d 100644
--- a/package.json
+++ b/package.json
@@ -1,5 +1,5 @@
{
- "name": "demo-storefront",
+ "name": "eject-cli",
"version": "0.0.1",
"license": "See license in LICENSE",
"engines": {
@@ -13,6 +13,22 @@
"devDependencies": {
"@salesforce/retail-react-app": "1.0.0-preview.0"
},
+ "files": [],
+ "oclif": {
+ "bin": "pwa-kit-dev",
+ "dirname": "generated-project",
+ "commands": "./commands",
+ "plugins": [
+ "@oclif/plugin-help",
+ "@oclif/plugin-plugins"
+ ],
+ "topicSeparator": " ",
+ "topics": {
+ "hello": {
+ "description": "Say hello to the world and others"
+ }
+ }
+ },
"scripts": {
"analyze-build": "cross-env MOBIFY_ANALYZE=true npm run build",
"build": "npm run build-translations && pwa-kit-dev build",
@@ -47,5 +63,12 @@
"iOS >= 9.0",
"Android >= 4.4.4",
"last 4 ChromeAndroid versions"
- ]
-}
\ No newline at end of file
+ ],
+ "dependencies": {
+ "@oclif/core": "^2.8.5",
+ "@oclif/plugin-help": "^5.2.9",
+ "@oclif/plugin-plugins": "^3.1.2",
+ "file-sync-cmp": "^0.1.1",
+ "glob": "^10.2.6"
+ }
+}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment