Skip to content

Commit

Permalink
Add @wordpress/scripts backbones.
Browse files Browse the repository at this point in the history
  • Loading branch information
msaggiorato committed Jun 25, 2021
1 parent bf76152 commit 8fb30b7
Show file tree
Hide file tree
Showing 12 changed files with 692 additions and 6 deletions.
1 change: 1 addition & 0 deletions .npmrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
package-lock=false
5 changes: 5 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# SAU/CAL Scaffold

This is meant to be a command to scaffold plugins, themes, projects with the command line.

_This package is inspired by [@wordpress/scripts](https://www.npmjs.com/package/@wordpress/scripts)._
9 changes: 9 additions & 0 deletions bin/sc-scaffold.js
Original file line number Diff line number Diff line change
@@ -1 +1,10 @@
#!/usr/bin/env node

/**
* Internal dependencies
*/
const { getNodeArgsFromCLI, spawnScript } = require( '../utils' );

const { scriptName, scriptArgs, nodeArgs } = getNodeArgsFromCLI();

spawnScript( scriptName, scriptArgs, nodeArgs );
5 changes: 0 additions & 5 deletions package-lock.json

This file was deleted.

33 changes: 32 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,5 +9,36 @@
"scaffold": "sc-scaffold"
},
"author": "SAU/CAL",
"license": "GPL-2.0-or-later"
"license": "GPL-2.0-or-later",
"keywords": [
"wordpress",
"scripts"
],
"homepage": "https://github.com/saucal/scaffold",
"repository": {
"type": "git",
"url": "https://github.com/saucal/scaffold.git"
},
"bugs": {
"url": "https://github.com/saucal/scaffold/issues"
},
"engines": {
"node": ">=12",
"npm": ">=6.9"
},
"files": [
"bin",
"config",
"scripts",
"utils"
],
"publishConfig": {
"access": "public"
},
"dependencies": {
"cross-spawn": "^5.1.0",
"minimist": "^1.2.5",
"read-pkg-up": "^1.0.1",
"resolve-bin": "^0.4.0"
}
}
99 changes: 99 additions & 0 deletions utils/cli.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
/**
* External dependencies
*/
const minimist = require( 'minimist' );
const spawn = require( 'cross-spawn' );

/**
* Internal dependencies
*/
const { fromScriptsRoot, hasScriptFile, getScripts } = require( './file' );
const { exit, getArgsFromCLI } = require( './process' );

const getArgFromCLI = ( arg ) => {
for ( const cliArg of getArgsFromCLI() ) {
const [ name, value ] = cliArg.split( '=' );
if ( name === arg ) {
return value || null;
}
}
};

const hasArgInCLI = ( arg ) => getArgFromCLI( arg ) !== undefined;

const getFileArgsFromCLI = () => minimist( getArgsFromCLI() )._;

const getNodeArgsFromCLI = () => {
const args = getArgsFromCLI();
const scripts = getScripts();
const scriptIndex = args.findIndex( ( arg ) => scripts.includes( arg ) );
return {
nodeArgs: args.slice( 0, scriptIndex ),
scriptName: args[ scriptIndex ],
scriptArgs: args.slice( scriptIndex + 1 ),
};
};

const hasFileArgInCLI = () => getFileArgsFromCLI().length > 0;

const handleSignal = ( signal ) => {
if ( signal === 'SIGKILL' ) {
// eslint-disable-next-line no-console
console.log(
'The script failed because the process exited too early. ' +
'This probably means the system ran out of memory or someone called ' +
'`kill -9` on the process.'
);
} else if ( signal === 'SIGTERM' ) {
// eslint-disable-next-line no-console
console.log(
'The script failed because the process exited too early. ' +
'Someone might have called `kill` or `killall`, or the system could ' +
'be shutting down.'
);
}
exit( 1 );
};

const spawnScript = ( scriptName, args = [], nodeArgs = [] ) => {
if ( ! scriptName ) {
// eslint-disable-next-line no-console
console.log( 'Script name is missing.' );
exit( 1 );
}

if ( ! hasScriptFile( scriptName ) ) {
// eslint-disable-next-line no-console
console.log(
'Unknown script "' +
scriptName +
'". ' +
'Perhaps you need to update the package?'
);
exit( 1 );
}

const { signal, status } = spawn.sync(
'node',
[ ...nodeArgs, fromScriptsRoot( scriptName ), ...args ],
{
stdio: 'inherit',
}
);

if ( signal ) {
handleSignal( signal );
}

exit( status );
};

module.exports = {
getArgFromCLI,
getArgsFromCLI,
getFileArgsFromCLI,
getNodeArgsFromCLI,
hasArgInCLI,
hasFileArgInCLI,
spawnScript,
};
147 changes: 147 additions & 0 deletions utils/config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,147 @@
/**
* External dependencies
*/
const { basename } = require( 'path' );

/**
* Internal dependencies
*/
const {
getArgsFromCLI,
getFileArgsFromCLI,
hasArgInCLI,
hasFileArgInCLI,
} = require( './cli' );
const { fromConfigRoot, fromProjectRoot, hasProjectFile } = require( './file' );
const { hasPackageProp } = require( './package' );

// See https://babeljs.io/docs/en/config-files#configuration-file-types
const hasBabelConfig = () =>
hasProjectFile( '.babelrc.js' ) ||
hasProjectFile( '.babelrc.json' ) ||
hasProjectFile( 'babel.config.js' ) ||
hasProjectFile( 'babel.config.json' ) ||
hasProjectFile( '.babelrc' ) ||
hasPackageProp( 'babel' );

/**
* Returns path to a Jest configuration which should be provided as the explicit
* configuration when there is none available for discovery by Jest in the
* project environment. Returns undefined if Jest should be allowed to discover
* an available configuration.
*
* This can be used in cases where multiple possible configurations are
* supported. Since Jest will only discover `jest.config.js`, or `jest` package
* directive, such custom configurations must be specified explicitly.
*
* @param {"e2e"|"unit"} suffix Suffix of configuration file to accept.
*
* @return {string=} Override or fallback configuration file path.
*/
function getJestOverrideConfigFile( suffix ) {
if ( hasArgInCLI( '-c' ) || hasArgInCLI( '--config' ) ) {
return;
}

if ( hasProjectFile( `jest-${ suffix }.config.js` ) ) {
return fromProjectRoot( `jest-${ suffix }.config.js` );
}

if ( ! hasJestConfig() ) {
return fromConfigRoot( `jest-${ suffix }.config.js` );
}
}

const hasJestConfig = () =>
hasProjectFile( 'jest.config.js' ) ||
hasProjectFile( 'jest.config.json' ) ||
hasPackageProp( 'jest' );

// See https://prettier.io/docs/en/configuration.html.
const hasPrettierConfig = () =>
hasProjectFile( '.prettierrc.js' ) ||
hasProjectFile( '.prettierrc.json' ) ||
hasProjectFile( '.prettierrc.toml' ) ||
hasProjectFile( '.prettierrc.yaml' ) ||
hasProjectFile( '.prettierrc.yml' ) ||
hasProjectFile( 'prettier.config.js' ) ||
hasProjectFile( '.prettierrc' ) ||
hasPackageProp( 'prettier' );

const hasWebpackConfig = () =>
hasArgInCLI( '--config' ) ||
hasProjectFile( 'webpack.config.js' ) ||
hasProjectFile( 'webpack.config.babel.js' );

// See https://github.com/michael-ciniawsky/postcss-load-config#usage (used by postcss-loader).
const hasPostCSSConfig = () =>
hasProjectFile( 'postcss.config.js' ) ||
hasProjectFile( '.postcssrc' ) ||
hasProjectFile( '.postcssrc.json' ) ||
hasProjectFile( '.postcssrc.yaml' ) ||
hasProjectFile( '.postcssrc.yml' ) ||
hasProjectFile( '.postcssrc.js' ) ||
hasPackageProp( 'postcss' );

/**
* Converts CLI arguments to the format which webpack understands.
*
* @see https://webpack.js.org/api/cli/#usage-with-config-file
*
* @return {Array} The list of CLI arguments to pass to webpack CLI.
*/
const getWebpackArgs = () => {
// Gets all args from CLI without those prefixed with `--webpack`.
let webpackArgs = getArgsFromCLI( [ '--webpack' ] );

const hasWebpackOutputOption =
hasArgInCLI( '-o' ) || hasArgInCLI( '--output' );
if ( hasFileArgInCLI() && ! hasWebpackOutputOption ) {
/**
* Converts a path to the entry format supported by webpack, e.g.:
* `./entry-one.js` -> `entry-one=./entry-one.js`
* `entry-two.js` -> `entry-two=./entry-two.js`
*
* @param {string} path The path provided.
*
* @return {string} The entry format supported by webpack.
*/
const pathToEntry = ( path ) => {
const entry = basename( path, '.js' );

if ( ! path.startsWith( './' ) ) {
path = './' + path;
}

return [ entry, path ].join( '=' );
};

// The following handles the support for multiple entry points in webpack, e.g.:
// `wp-scripts build one.js custom=./two.js` -> `webpack one=./one.js custom=./two.js`
webpackArgs = webpackArgs.map( ( cliArg ) => {
if (
getFileArgsFromCLI().includes( cliArg ) &&
! cliArg.includes( '=' )
) {
return pathToEntry( cliArg );
}

return cliArg;
} );
}

if ( ! hasWebpackConfig() ) {
webpackArgs.push( '--config', fromConfigRoot( 'webpack.config.js' ) );
}

return webpackArgs;
};

module.exports = {
getWebpackArgs,
hasBabelConfig,
getJestOverrideConfigFile,
hasJestConfig,
hasPrettierConfig,
hasPostCSSConfig,
};
39 changes: 39 additions & 0 deletions utils/file.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/**
* External dependencies
*/
const { existsSync, readdirSync } = require( 'fs' );
const path = require( 'path' );

/**
* Internal dependencies
*/
const { getPackagePath } = require( './package' );

const fromProjectRoot = ( fileName ) =>
path.join( path.dirname( getPackagePath() ), fileName );

const hasProjectFile = ( fileName ) =>
existsSync( fromProjectRoot( fileName ) );

const fromConfigRoot = ( fileName ) =>
path.join( path.dirname( __dirname ), 'config', fileName );

const fromScriptsRoot = ( scriptName ) =>
path.join( path.dirname( __dirname ), 'scripts', `${ scriptName }.js` );

const hasScriptFile = ( scriptName ) =>
existsSync( fromScriptsRoot( scriptName ) );

const getScripts = () =>
readdirSync( path.join( path.dirname( __dirname ), 'scripts' ) )
.filter( ( f ) => path.extname( f ) === '.js' )
.map( ( f ) => path.basename( f, '.js' ) );

module.exports = {
fromProjectRoot,
fromConfigRoot,
fromScriptsRoot,
getScripts,
hasProjectFile,
hasScriptFile,
};
43 changes: 43 additions & 0 deletions utils/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
/**
* Internal dependencies
*/
const {
getArgFromCLI,
getArgsFromCLI,
getFileArgsFromCLI,
getNodeArgsFromCLI,
hasArgInCLI,
hasFileArgInCLI,
spawnScript,
} = require( './cli' );
const {
getWebpackArgs,
hasBabelConfig,
getJestOverrideConfigFile,
hasJestConfig,
hasPrettierConfig,
hasPostCSSConfig,
} = require( './config' );
const { fromProjectRoot, fromConfigRoot, hasProjectFile } = require( './file' );
const { getPackageProp, hasPackageProp } = require( './package' );

module.exports = {
fromProjectRoot,
fromConfigRoot,
getArgFromCLI,
getArgsFromCLI,
getFileArgsFromCLI,
getJestOverrideConfigFile,
getNodeArgsFromCLI,
getPackageProp,
getWebpackArgs,
hasArgInCLI,
hasBabelConfig,
hasFileArgInCLI,
hasJestConfig,
hasPackageProp,
hasPostCSSConfig,
hasPrettierConfig,
hasProjectFile,
spawnScript,
};
Loading

0 comments on commit 8fb30b7

Please sign in to comment.