Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
80 changes: 63 additions & 17 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
# Building SiteOrigin plugins
There are few steps necessary to prepare a plugin for release on the WordPress.org plugin directory. We use [Gulp](http://gulpjs.com/) to automate this.
There are few steps necessary to prepare a plugin for release on the WordPress.org plugin directory. We use [Gulp 5](http://gulpjs.com/) to automate this.

## Environment setup
1. [Download](https://nodejs.org/download/) and install Node.js and npm.
2. Install gulp using `npm install -g gulp`.
1. [Download](https://nodejs.org/download/) and install Node.js and npm (Node.js 18+ required for Gulp 5).
2. Install Gulp CLI using `npm install -g gulp-cli`.
3. In the plugin folder, ensure the plugin-build repository has been added as a submodule in a folder called 'build' and is up to date. This can be done using `git submodule add git@github.com:siteorigin/plugin-build.git build`.
4. In a terminal, navigate to the build directory in the plugin and run `npm install`. When using plugin-build in the [SiteOrigin CSS](https://github.com/siteorigin/so-css) plugin, `npm-install` should be run in both the SiteOrigin CSS plugin folder and the build folder.
4. In a terminal, navigate to the build directory in the plugin and run `npm install`. When using plugin-build in the [SiteOrigin CSS](https://github.com/siteorigin/so-css) plugin, `npm install` should be run in both the SiteOrigin CSS plugin folder and the build folder.
5. Get some coffee while npm installs the required packages.

## Configuring builds
Expand All @@ -29,30 +29,76 @@ module.exports = {
```

## Running builds
There are two build tasks, `build:release` and `build:dev`.
There are two main build tasks, `build:release` and `build:dev`.

The release task performs the following subtasks:

1. Updates the version number in the required files.
2. Compiles required SASS and LESS files to CSS.
3. Minifies required JavaScript files and adds the suffix specified in the `build-config.js` file.
4. Copies all files to a `dist/` folder.
5. Creates a `.zip` archive with the appropriate filename ready for uploading to wordpress.org.
3. Processes JavaScript files with Babel and Browserify.
4. Minifies CSS and JavaScript files.
5. Copies all files to a `dist/` folder.
6. Generates translation (POT) files.
7. Creates a `.zip` archive with the appropriate filename ready for uploading to wordpress.org.

Release task usage:

`gulp build:release -v {version}`
```bash
npm run build:release --release=1.2.3
```

The development build task includes:

1. Initial compilation of LESS/SASS files to CSS.
2. Initial processing of JavaScript files.
3. Watch LESS, SASS, and JavaScript files for changes and recompile automatically.

Development task usage:

```bash
npm run build:dev
```

## Individual tasks

You can also run individual build tasks:

```bash
npm run css # Compile LESS/SASS files
npm run js # Process JavaScript files
npm run minify # Minify CSS and JS files
npm run copy # Copy files to tmp directory
npm run i18n # Generate translation files
```

## Special tasks

### Updating Font Awesome
```bash
npm run update:font-awesome
```

### Updating the Google fonts array
```bash
npx cross-env gulp updateGoogleFontsTask --apiKey=YOUR_API_KEY
```

The Google Fonts task will require an update to the build-config file in each plugin to specify the name and location of the fonts file.

Where `{version}` should be replaced with the required version number.
For example, say the next version of the plugin is 1.2.3:
## Gulp 5 Migration Notes

`gulp build:release -v {1.2.3}`
This build system has been migrated from Gulp 3 to Gulp 5 with the following key changes:

The dev build task only has one subtask:
- **ES Modules**: All tasks now use ES module syntax instead of CommonJS.
- **Task Binding**: Individual tasks require explicit binding for proper configuration passing.
- **Copy Patterns**: Updated glob patterns for better file selection and exclusion handling.
- **Dependencies**: Updated to use `gulp-terser` instead of `gulp-uglify-es` and modern package versions.

1) Watch LESS and/or SASS files for changes and compile to CSS.
## Troubleshooting

This is simply to avoid having to manually recompile LESS/SASS files while working on them.
If you encounter issues:

## Updating the Google fonts array
`gulp updateGoogleFonts ---apiKey {YOUR_API_KEY}` The task will require an update to the build-config file in each plugin to specify the name and location of the fonts file.
1. Ensure Node.js 18+ is installed.
2. Clear `node_modules` and run `npm install` again.
3. Check that all required files exist in the plugin's `build-config.js`.
4. Verify that the build directory has proper read/write permissions.
67 changes: 67 additions & 0 deletions build-steps/css-tasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
// css-tasks.js
import gulp from 'gulp';
const { src, dest } = gulp;
import less from 'gulp-less';
import sass from 'gulp-sass';

export const lessTask = (config, args) => {
if (!config.less || !config.less.src || config.less.src.length === 0) {
console.log('No LESS configuration found or empty source files');
return Promise.resolve();
}

console.log('LESS task starting...');
console.log('LESS src patterns:', config.less.src);

// Check if this is a release build (has version/release arg) vs dev build
const isReleaseBuild = args.release || args._[0] === 'buildRelease';
const outputDir = isReleaseBuild ? 'tmp' : '.';
const compress = isReleaseBuild;

console.log('LESS output dir:', outputDir);
console.log('LESS compress:', compress);

return src(config.less.src, { base: '.' })
.pipe(less({
paths: config.less.include,
compress: compress
}).on('error', (err) => {
console.error('LESS compilation error:', err.message);
// Don't exit in dev mode, just log the error
if (isReleaseBuild) {
process.exit(1);
}
}))
.pipe(dest(outputDir));
};

export const sassTask = (config, args) => {
if (!config.sass || !config.sass.src || config.sass.src.length === 0) {
console.log('No SASS configuration found or empty source files');
return Promise.resolve();
}

console.log('SASS task starting...');
console.log('SASS src patterns:', config.sass.src);

// Check if this is a release build (has version/release arg) vs dev build
const isReleaseBuild = args.release || args._[0] === 'buildRelease';
const outputDir = isReleaseBuild ? 'tmp' : '.';
const outputStyle = isReleaseBuild ? 'compressed' : 'nested';

console.log('SASS output dir:', outputDir);
console.log('SASS output style:', outputStyle);

return src(config.sass.src, { base: '.' })
.pipe(sass({
includePaths: config.sass.include,
outputStyle: outputStyle
}).on('error', (err) => {
console.error('SASS compilation error:', err.message);
// Don't exit in dev mode, just log the error
if (isReleaseBuild) {
process.exit(1);
}
}))
.pipe(dest(outputDir));
};
74 changes: 74 additions & 0 deletions build-steps/js-tasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
// js-tasks.js
import gulp from 'gulp';
const { src, dest } = gulp;
import babel from 'gulp-babel';
import browserify from 'browserify';
import source from 'vinyl-source-stream';

export const babelTask = (config, args) => {
if (typeof config.babel === 'undefined') {
console.log('No Babel configuration found');
return Promise.resolve();
}

console.log('Babel task starting...');
console.log('Babel src patterns:', config.babel.src);

// Check if this is a release build (has version/release arg) vs dev build
const isReleaseBuild = args.release || args._[0] === 'buildRelease';
const outputDir = isReleaseBuild ? 'tmp' : '.';

console.log('Babel output dir:', outputDir);

return src(config.babel.src, { base: '.' })
.on('data', (file) => {
console.log('Processing Babel file:', file.path);
})
.pipe(babel({
cwd: 'build',
presets: [
"@babel/preset-env",
"@babel/preset-react",
],
}).on('error', (err) => {
console.error('Babel compilation error:', err.message);
if (isReleaseBuild) {
process.exit(1);
}
}))
.pipe(dest(outputDir));
};

export const browserifyTask = (config) => {
if (typeof config.browserify === 'undefined') {
console.log('No Browserify configuration found');
return Promise.resolve();
}

console.log('Browserify task starting...');

const runBrowserify = (browserifyConfig) => {
console.log('Processing Browserify bundle:', browserifyConfig.fileName);

return browserify(browserifyConfig.src)
.bundle()
.on('error', function(err) {
console.error('Browserify error:', err.message);
this.emit('end');
})
.pipe(source(browserifyConfig.fileName))
.pipe(dest(browserifyConfig.dest));
};

if (Array.isArray(config.browserify)) {
console.log('Processing multiple Browserify configurations:', config.browserify.length);
let browserifyOutput;
for (let i = 0; i < config.browserify.length; i++) {
browserifyOutput = runBrowserify(config.browserify[i]);
}
return browserifyOutput;
} else {
console.log('Processing single Browserify configuration');
return runBrowserify(config.browserify);
}
};
99 changes: 99 additions & 0 deletions build-steps/minify-tasks.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
// minify-tasks.js
import gulp from 'gulp';
const { src, dest } = gulp;
import cssnano from 'gulp-cssnano';
import rename from 'gulp-rename';
import terser from 'gulp-terser';
import filter from 'gulp-filter';

export const minifyCss = (config, args) => {
if (!config.css || !config.css.src || config.css.src.length === 0) {
console.log('No CSS minification configuration found.');
return Promise.resolve();
}

console.log('CSS minify task starting...');
console.log('CSS minify src patterns:', config.css.src);

// Check if this is a release build vs dev build.
const isReleaseBuild = args.release || args._[0] === 'buildRelease';
const outputDir = isReleaseBuild ? 'tmp' : '.';

console.log('CSS minify output dir:', outputDir);

return src(config.css.src, { base: '.' })
.on('data', (file) => {
console.log('Processing CSS minify file:', file.path);
})
.pipe(cssnano({
discardComments: {
removeAll: true
}
}).on('error', (err) => {
console.error('CSS minification error:', err.message);
if (isReleaseBuild) {
process.exit(1);
}
}))
.pipe(rename((path) => {
path.extname = '.min.css';
}))
.pipe(dest(outputDir));
};

export const minifyJs = (config, jsMinSuffix) => {
if (!config.js || !config.js.src || config.js.src.length === 0) {
console.log('No JS minification configuration found.');
return Promise.resolve();
}

console.log('JS minify task starting...');
console.log('JS minify src patterns:', config.js.src);
console.log('JS minify suffix:', jsMinSuffix);

// Two-step process for JS minification.
return new Promise((resolve, reject) => {
console.log('Step 1: Copying original JS files to tmp/');

// First step: Copy original JS files to tmp/.
const copyOriginals = src(config.js.src, { base: '.' })
.on('data', (file) => {
console.log('Copying original JS file:', file.path);
})
.pipe(dest('tmp'));

copyOriginals.on('end', () => {
console.log('Step 2: Creating minified versions');

// Second step: Create minified versions.
const createMinified = src(config.js.src, { base: '.' })
.on('data', (file) => {
console.log('Processing JS minify file:', file.path);
})
.pipe(filter('**/*.js'))
.pipe(terser({
mangle: true,
compress: {
drop_console: false
}
}).on('error', (err) => {
console.error('JS minification error:', err.message);
reject(err);
}))
.pipe(rename((path) => {
const baseName = path.basename;
path.basename = baseName + jsMinSuffix;
}))
.pipe(dest('tmp'));

createMinified.on('end', () => {
console.log('JS minification complete.');
resolve();
});

createMinified.on('error', reject);
});

copyOriginals.on('error', reject);
});
};
Loading