diff --git a/.circleci/config.yml b/.circleci/config.yml index 236e308..6942be1 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -4,6 +4,42 @@ orbs: node: circleci/node@4.7.0 jobs: + install_tests: + docker: + - image: cimg/node:17.2.0 + steps: + - checkout + - restore_cache: + name: Restoring Modules Cache + keys: + - v1-modules-cache-{{ checksum "package-lock.json" }} + - node/install-packages: + pkg-manager: npm + - run: + command: | + cd ./example-site/ + node ./../src/cli.js install + name: Run test - install projects + - run: + command: | + cd ./example-site/ + node ./../src/cli.js install -- --package-lock-only + name: Run test - install projects with npm args + - run: + command: | + cd ./example-site/ + node ./../src/cli.js ci + name: Run test - ci install projects + - save_cache: + name: Saving Modules Cache + key: v1-modules-cache-{{ checksum "package-lock.json" }} + paths: + - ./node_modules + - /home/circleci/.cache + - persist_to_workspace: + root: ~/project + paths: + - . notify_slack: docker: - image: cimg/node:17.2.0 @@ -64,6 +100,11 @@ jobs: cd ./example-site/ node ./../src/cli.js ci name: Run test - ci install projects + - run: + command: | + cd ./example-site/ + node ./../src/cli.js ci + name: Run test - ci install projects - run: command: | cd ./example-site/ @@ -84,7 +125,10 @@ jobs: paths: - . workflows: - test_build_tools: + test_install: + jobs: + - install_tests + test_build: jobs: - install_tests - self_test diff --git a/example-site/client-mu-plugins/test-client-plugin/package-lock.json b/example-site/client-mu-plugins/test-client-plugin/package-lock.json new file mode 100644 index 0000000..f7c7807 --- /dev/null +++ b/example-site/client-mu-plugins/test-client-plugin/package-lock.json @@ -0,0 +1,4 @@ +{ + "name": "test-client-plugin", + "lockfileVersion": 1 +} diff --git a/example-site/client-mu-plugins/test-client-plugin/package.json b/example-site/client-mu-plugins/test-client-plugin/package.json index 922ad92..ca9adcc 100644 --- a/example-site/client-mu-plugins/test-client-plugin/package.json +++ b/example-site/client-mu-plugins/test-client-plugin/package.json @@ -1,3 +1,3 @@ { "name": "test-client-plugin" -} \ No newline at end of file +} diff --git a/example-site/plugins/test-plugin/package-lock.json b/example-site/plugins/test-plugin/package-lock.json new file mode 100644 index 0000000..f61a641 --- /dev/null +++ b/example-site/plugins/test-plugin/package-lock.json @@ -0,0 +1,4 @@ +{ + "name": "test-plugin", + "lockfileVersion": 1 +} diff --git a/example-site/plugins/test-plugin/package.json b/example-site/plugins/test-plugin/package.json index 46b0f24..a77e8f0 100644 --- a/example-site/plugins/test-plugin/package.json +++ b/example-site/plugins/test-plugin/package.json @@ -1,3 +1,3 @@ { "name": "test-plugin" -} \ No newline at end of file +} diff --git a/example-site/plugins/test-plugin/src/entrypoints/frontend.js b/example-site/plugins/test-plugin/src/entrypoints/frontend.js new file mode 100644 index 0000000..5d5e50f --- /dev/null +++ b/example-site/plugins/test-plugin/src/entrypoints/frontend.js @@ -0,0 +1 @@ +console.log('test frontend'); diff --git a/example-site/themes/test-theme/package-lock.json b/example-site/themes/test-theme/package-lock.json new file mode 100644 index 0000000..49dadc6 --- /dev/null +++ b/example-site/themes/test-theme/package-lock.json @@ -0,0 +1,4 @@ +{ + "name": "test-theme", + "lockfileVersion": 1 +} diff --git a/example-site/themes/test-theme/package.json b/example-site/themes/test-theme/package.json index bca1941..b2a68ef 100644 --- a/example-site/themes/test-theme/package.json +++ b/example-site/themes/test-theme/package.json @@ -1,3 +1,3 @@ { "name": "test-theme" -} \ No newline at end of file +} diff --git a/package-lock.json b/package-lock.json index 4df5932..0ad185a 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@bigbite/build-tools", - "version": "1.0.0", + "version": "1.1.0", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@bigbite/build-tools", - "version": "1.0.0", + "version": "1.1.0", "license": "MIT", "dependencies": { "@babel/core": "^7.14.8", @@ -4275,9 +4275,9 @@ } }, "node_modules/decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=", + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==", "engines": { "node": ">=0.10" } @@ -7495,9 +7495,9 @@ } }, "node_modules/loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -12614,9 +12614,9 @@ } }, "node_modules/svg-baker/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -12748,9 +12748,9 @@ } }, "node_modules/svg-sprite-loader/node_modules/loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "dependencies": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -16917,9 +16917,9 @@ } }, "decode-uri-component": { - "version": "0.2.0", - "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz", - "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=" + "version": "0.2.2", + "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.2.tgz", + "integrity": "sha512-FqUYQ+8o158GyGTrMFJms9qh3CqTKvAqgqsTnkLI8sKu0028orqBhxNMFkFen0zGyg6epACD32pjVk58ngIErQ==" }, "deep-is": { "version": "0.1.4", @@ -19262,9 +19262,9 @@ "integrity": "sha512-3R/1M+yS3j5ou80Me59j7F9IMs4PXs3VqRrm0TU3AbKPxlmpoY1TNscJV/oGJXo8qCatFGTfDbY6W6ipGOYXfg==" }, "loader-utils": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.2.tgz", - "integrity": "sha512-TM57VeHptv569d/GKh6TAYdzKblwDNiumOdkFnejjD0XwTH87K90w3O7AiJRqdQoXygvi1VQTJTLGhJl7WqA7A==", + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-2.0.4.tgz", + "integrity": "sha512-xXqpXoINfFhgua9xiqD8fPFHgkoq1mmmpE92WlDbm9rNRd/EbRb+Gqf908T2DMfuHjjJlksiK2RbHVOdD/MqSw==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -23005,9 +23005,9 @@ "integrity": "sha512-NGEErnH6F2vUuXDh+OlbcKW7/wOcfdRHaZ7VWtqCztfHri/++YKmP51OdWeGPuqCOba6kk2OTe5d02VmTB80Pw==" }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", @@ -23128,9 +23128,9 @@ } }, "loader-utils": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.0.tgz", - "integrity": "sha512-qH0WSMBtn/oHuwjy/NucEgbx5dbxxnxup9s4PVXJUDHZBQY+s0NWA9rJf53RBnQZxfch7euUui7hpoAPvALZdA==", + "version": "1.4.2", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.4.2.tgz", + "integrity": "sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==", "requires": { "big.js": "^5.2.2", "emojis-list": "^3.0.0", diff --git a/package.json b/package.json index 0d505e1..ffdbfd2 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@bigbite/build-tools", - "version": "1.0.0", + "version": "1.1.0", "description": "Provides configuration for the Big Bite Build Tools.", "author": "Paul Taylor (https://github.com/ampersarnie)", "keywords": [ diff --git a/src/cli.js b/src/cli.js index fb9bebb..f33260c 100644 --- a/src/cli.js +++ b/src/cli.js @@ -1,6 +1,7 @@ #!/usr/bin/env node const yargs = require('yargs'); +const globalVars = require('./globals'); yargs.scriptName('build-tools'); yargs.usage('Usage: build-tools '); diff --git a/src/commands/build.js b/src/commands/build.js index 31364f1..ad96cd1 100644 --- a/src/commands/build.js +++ b/src/commands/build.js @@ -59,7 +59,6 @@ exports.handler = async ({ const mode = production ? 'production' : 'development'; // Use env variables if working on Webpack >=5. const projectsList = projects.split(',').filter((item) => item.length > 0); - const targetDirs = ['client-mu-plugins', 'plugins', 'themes']; const hasTargetDirs = dirsExist(targetDirs); const isAllProjects = (site || hasTargetDirs) && !projects; @@ -95,10 +94,8 @@ exports.handler = async ({ } terminal('Processing the following projects:\n'); - packages.forEach((item) => { - const regexDirs = targetDirs.join('|'); - const packagePath = item.packagePath.match(`((${regexDirs})\/)?([^\/]+)\/package.json$`); - terminal.defaultColor(` * %s `, item.packageName).dim(`[%s]\n`, packagePath[0]); + packages.forEach((package) => { + terminal.defaultColor(` * %s `, package.name).dim(`[%s]\n`, package.relativePath); }); terminal('\n'); @@ -111,8 +108,8 @@ exports.handler = async ({ * to build them, use what is here. */ const PROJECT_CONFIG = { - name: packageObject.packageName, - version: packageObject.package.version, + name: packageObject.name, + version: packageObject.json.version, paths: { project: path.resolve(packageObject.path), config: path.resolve(`${__dirname}/configs`), @@ -131,18 +128,18 @@ exports.handler = async ({ }; let customWebpackConfig = { - extends: true + extends: true, }; let config = webpackConfig(PROJECT_CONFIG, mode); try { customWebpackConfig = { ...customWebpackConfig, - ...require(PROJECT_CONFIG.paths.project + '/webpack.config.js') - } - } catch(e) {} + ...require(PROJECT_CONFIG.paths.project + '/webpack.config.js'), + }; + } catch (e) {} - if(!customWebpackConfig?.extends) { + if (!customWebpackConfig?.extends) { config = customWebpackConfig; } else if (customWebpackConfig) { config = { diff --git a/src/commands/ci.js b/src/commands/ci.js new file mode 100644 index 0000000..4ad43ae --- /dev/null +++ b/src/commands/ci.js @@ -0,0 +1,9 @@ +const installer = require('./installer/installer'); + +exports.command = 'ci'; +exports.desc = 'Run an npm ci install process.'; +exports.builder = (yargs) => {}; + +exports.handler = async ({}) => { + installer(exports.command); +}; diff --git a/src/commands/install.js b/src/commands/install.js new file mode 100644 index 0000000..b6c39ef --- /dev/null +++ b/src/commands/install.js @@ -0,0 +1,9 @@ +const installer = require('./installer/installer'); + +exports.command = 'install'; +exports.desc = 'Run an npm install process.'; +exports.builder = (yargs) => {}; + +exports.handler = async ({}) => { + installer(exports.command); +}; diff --git a/src/commands/installer/installer.js b/src/commands/installer/installer.js new file mode 100644 index 0000000..8f95bab --- /dev/null +++ b/src/commands/installer/installer.js @@ -0,0 +1,48 @@ +const { terminal } = require('terminal-kit'); +const { execSync } = require('child_process'); +const yargs = require('yargs'); +const args = yargs.argv._; + +const { findAllProjectPaths } = require('./../../utils/projectpaths'); +const { getPackage } = require('./../../utils/get-package'); +const dirsExist = require('./../../utils/dirs-exist'); + +module.exports = (commandType) => { + if (!['ci', 'install'].includes(commandType)) { + throw new Error('Not a valid comment type.'); + } + + const hasTargetDirs = dirsExist(targetDirs); + + if (!hasTargetDirs) { + throw new Error('Recursive install only works from the site root directory.'); + } + + let paths = findAllProjectPaths(targetDirs); + + let packages = []; + + try { + packages = paths.map((item) => getPackage(item, false)).filter((item) => item); + } catch (e) { + terminal.red(e); + process.exit(1); + } + + terminal('Installing packages for the following projects:\n'); + packages.forEach((package) => { + terminal.defaultColor(` * %s `, package.name).dim(`[%s]\n`, package.relativePath); + }); + terminal('\n'); + + const gluedArgs = args.join(' '); + + packages.forEach((package) => { + terminal + .defaultColor(`Installing packages for `) + .bold(`%s`, package.name) + .dim(` [%s]\n`, package.relativePath); + execSync(`npm ${gluedArgs}`, { cwd: package.path, stdio: 'inherit' }); + terminal.defaultColor('\n\n----------------------\n\n'); + }); +}; diff --git a/src/globals.js b/src/globals.js new file mode 100644 index 0000000..1553bae --- /dev/null +++ b/src/globals.js @@ -0,0 +1,2 @@ +global.targetDirs = ['client-mu-plugins', 'plugins', 'themes']; +global.packageList = {}; diff --git a/src/utils/get-package.js b/src/utils/get-package.js index c9e078a..df3837d 100644 --- a/src/utils/get-package.js +++ b/src/utils/get-package.js @@ -1,11 +1,21 @@ const fs = require('fs'); +/** + * Retrieves the package.json file from a given directory and compiles it + * along with additional data into a parse-able format by the rest of the script. + * + * @param {string} path The project directory path where package.json is expected. + * @param {boolean} throwError Whether to throw errors when package.json does not exist. + * @returns + */ const getPackage = (path, throwError = true) => { - let packageName = ''; + const absolutePath = `${path}/package.json`; - const packagePath = `${path}/package.json`; + if (packageList[absolutePath]) { + return packageList[absolutePath]; + } - if (!fs.existsSync(packagePath)) { + if (!fs.existsSync(absolutePath)) { if (throwError) { throw new Error( `package.json does not exist for this project.\n\nPlease create one in: ${path}`, @@ -15,17 +25,25 @@ const getPackage = (path, throwError = true) => { return false; } - const packageJSON = JSON.parse(fs.readFileSync(packagePath)); - const packageObject = packageJSON === Object(packageJSON) ? packageJSON : {}; + const json = JSON.parse(fs.readFileSync(absolutePath)); + const packageObject = json === Object(json) ? json : {}; const packageNames = packageObject?.name?.split('/'); - packageName = packageNames?.[packageNames.length - 1] || ''; + const name = packageNames?.[packageNames.length - 1] || ''; + + const regexDirs = targetDirs.join('|'); + const packagePath = absolutePath.match(`((${regexDirs})\/)?([^\/]+)\/package.json$`); - return { + const packageValues = { path, - packagePath, - packageName, - package: packageJSON, + absolutePath, + relativePath: packagePath[0], + name, + json, }; + + packageList[absolutePath] = packageValues; + + return packageValues; }; module.exports = {