woocommerce/plugins/woocommerce-admin/bin/packages/build.js

274 lines
7.2 KiB
JavaScript
Executable File

/**
* script to build packages into `build/` directory.
*
* Example:
* node ./bin/packages/build.js
*/
/**
* External dependencies
*/
const fs = require( 'fs' );
const path = require( 'path' );
const glob = require( 'glob' );
const babel = require( '@babel/core' );
const chalk = require( 'chalk' );
const mkdirp = require( 'mkdirp' );
const sass = require( 'node-sass' );
const postcss = require( 'postcss' );
const deasync = require( 'deasync' );
const rimraf = require( 'rimraf' );
/**
* Internal dependencies
*/
const getPackages = require( './get-packages' );
const getBabelConfig = require( './get-babel-config' );
const { exit } = require( 'process' );
/**
* Module Constants
*/
const PACKAGES_DIR = path.resolve( __dirname, '../../packages' );
const SRC_DIR = 'src';
const BUILD_DIR = {
main: 'build',
module: 'build-module',
style: 'build-style',
};
const DONE = chalk.reset.inverse.bold.green( ' DONE ' );
/**
* Get the package name for a specified file
*
* @param {string} file File name
* @return {string} Package name
*/
function getPackageName( file ) {
return path.relative( PACKAGES_DIR, file ).split( path.sep )[ 0 ];
}
const isJsFile = ( filepath ) => {
return /.\.js$/.test( filepath );
};
const isScssFile = ( filepath ) => {
return /.\.scss$/.test( filepath );
};
/**
* Get Build Path for a specified file
*
* @param {string} file File to build
* @param {string} buildFolder Output folder
* @return {string} Build path
*/
function getBuildPath( file, buildFolder ) {
const pkgName = getPackageName( file );
const pkgSrcPath = path.resolve( PACKAGES_DIR, pkgName, SRC_DIR );
const pkgBuildPath = path.resolve( PACKAGES_DIR, pkgName, buildFolder );
const relativeToSrcPath = path.relative( pkgSrcPath, file );
return path.resolve( pkgBuildPath, relativeToSrcPath );
}
/**
* Given a list of scss and js filepaths, divide them into sets them and rebuild.
*
* @param {Array} files list of files to rebuild
*/
function buildFiles( files ) {
// Reduce files into a unique sets of javaScript files and scss packages.
const buildPaths = files.reduce(
( accumulator, filePath ) => {
if ( isJsFile( filePath ) ) {
accumulator.jsFiles.add( filePath );
} else if ( isScssFile( filePath ) ) {
const pkgName = getPackageName( filePath );
const pkgPath = path.resolve( PACKAGES_DIR, pkgName );
accumulator.scssPackagePaths.add( pkgPath );
}
return accumulator;
},
{ jsFiles: new Set(), scssPackagePaths: new Set() }
);
buildPaths.jsFiles.forEach( buildJsFile );
buildPaths.scssPackagePaths.forEach( buildPackageScss );
}
/**
* Build a javaScript file for the required environments (node and ES5)
*
* @param {string} file File path to build
* @param {boolean} silent Show logs
*/
function buildJsFile( file, silent ) {
buildJsFileFor( file, silent, 'main' );
buildJsFileFor( file, silent, 'module' );
}
/**
* Build a package's scss styles
*
* @param {string} packagePath The path to the package.
*/
function buildPackageScss( packagePath ) {
const srcDir = path.resolve( packagePath, SRC_DIR );
const scssFiles = glob.sync( `${ srcDir }/*.scss` );
// Build scss files individually.
scssFiles.forEach( buildScssFile );
}
function buildScssFile( styleFile ) {
const outputFile = getBuildPath(
styleFile.replace( '.scss', '.css' ),
BUILD_DIR.style
);
const outputFileRTL = getBuildPath(
styleFile.replace( '.scss', '-rtl.css' ),
BUILD_DIR.style
);
mkdirp.sync( path.dirname( outputFile ) );
const builtSass = sass.renderSync( {
file: styleFile,
includePaths: [
path.resolve( __dirname, '../../client/stylesheets/abstracts' ),
],
data:
[ 'colors', 'variables', 'breakpoints', 'mixins' ]
.map( ( imported ) => `@import "_${ imported }";` )
.join( ' ' ) + fs.readFileSync( styleFile, 'utf8' ),
} );
const postCSSConfig = require( '@wordpress/postcss-plugins-preset' );
const postCSSSync = ( callback ) => {
postcss( postCSSConfig )
.process( builtSass.css, {
from: 'src/app.css',
to: 'dest/app.css',
} )
.then( ( result ) => callback( null, result ) );
};
const postCSSRTLSync = ( ltrCSS, callback ) => {
postcss( [ require( 'rtlcss' )() ] )
.process( ltrCSS, { from: 'src/app.css', to: 'dest/app.css' } )
.then( ( result ) => callback( null, result ) );
};
const result = deasync( postCSSSync )();
fs.writeFileSync( outputFile, result.css );
const resultRTL = deasync( postCSSRTLSync )( result );
fs.writeFileSync( outputFileRTL, resultRTL.css );
}
/**
* Build a file for a specific environment
*
* @param {string} file File path to build
* @param {boolean} silent Show logs
* @param {string} environment Dist environment (node or es5)
*/
function buildJsFileFor( file, silent, environment ) {
const buildDir = BUILD_DIR[ environment ];
const destPath = getBuildPath( file, buildDir );
const babelOptions = getBabelConfig( environment );
babelOptions.sourceMaps = true;
babelOptions.sourceFileName = file;
mkdirp.sync( path.dirname( destPath ) );
const transformed = babel.transformFileSync( file, babelOptions );
fs.writeFileSync( destPath + '.map', JSON.stringify( transformed.map ) );
fs.writeFileSync(
destPath,
transformed.code +
'\n//# sourceMappingURL=' +
path.basename( destPath ) +
'.map'
);
if ( ! silent ) {
process.stdout.write(
chalk.green( ' \u2022 ' ) +
path.relative( PACKAGES_DIR, file ) +
chalk.green( ' \u21D2 ' ) +
path.relative( PACKAGES_DIR, destPath ) +
'\n'
);
}
}
/**
* Build the provided package path
*
* @param {string} packagePath absolute package path
*/
function buildPackage( packagePath ) {
const srcDir = path.resolve( packagePath, SRC_DIR );
const jsFiles = glob.sync( `${ srcDir }/**/*.js`, {
ignore: [
`${ srcDir }/**/test/**/*.js`,
`${ srcDir }/**/__mocks__/**/*.js`,
],
nodir: true,
} );
process.stdout.write( `${ path.basename( packagePath ) }\n` );
// Build js files individually.
jsFiles.forEach( ( file ) => buildJsFile( file, true ) );
// Build package CSS files
buildPackageScss( packagePath );
process.stdout.write( `${ DONE }\n` );
}
/**
* Build array of packages for consumption for Dependency Extraction
*/
function buildDependencyExtractionAssets() {
const packages = getPackages();
const packageNames = packages.map( ( packagePath ) => {
return `\n\t'@woocommerce/${ path.basename( packagePath ) }'`;
} );
const contents = `module.exports = [${ packageNames },\n];\n`;
const DEPENDENCY_EXTRACTION_ASSETS = path.join(
path.resolve(
__dirname,
'../../packages/dependency-extraction-webpack-plugin'
),
'assets'
);
try {
rimraf( DEPENDENCY_EXTRACTION_ASSETS, () => {
mkdirp.sync( DEPENDENCY_EXTRACTION_ASSETS );
fs.writeFileSync(
path.join( DEPENDENCY_EXTRACTION_ASSETS, 'packages.js' ),
contents
);
} );
} catch ( err ) {
console.log( err );
exit( 1 );
}
}
const files = process.argv.slice( 2 );
if ( files.length ) {
buildFiles( files );
} else {
process.stdout.write( chalk.inverse( '>> Building packages \n' ) );
buildDependencyExtractionAssets();
getPackages()
.filter(
( package ) =>
! /dependency-extraction-webpack-plugin/.test( package )
)
.forEach( buildPackage );
process.stdout.write( '\n' );
}