Skip to content

Commit

Permalink
Merge pull request #120 from bigbite/feature/targeted-entrypoints
Browse files Browse the repository at this point in the history
Add support for declaring specific endpoints to be built
  • Loading branch information
ampersarnie authored Nov 24, 2024
2 parents 4fe5b22 + eedd67d commit 6830250
Show file tree
Hide file tree
Showing 9 changed files with 151 additions and 13 deletions.
42 changes: 42 additions & 0 deletions __tests__/cli.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -170,6 +170,48 @@ describe('CLI Build Command', () => {
expect(process.stdout.write).toHaveBeenCalledWith(` * my-theme `);
});

it('runs specific projects and entrypoints mode when requested', () => {
mockFs({
...requiredRealDirs,
plugins: {
'my-plugin': {
'package.json': JSON.stringify({
name: 'my-plugin',
}),
src: {
entrypoints: {
'some-file.js': 'console.log("file content here");',
'frontend.js': 'console.log("file content here");',
},
},
},
},
themes: {
'my-theme': {
'package.json': JSON.stringify({
name: 'my-theme',
}),
src: {
entrypoints: {
'some-file.js': 'console.log("file content here");',
},
},
},
},
'client-mu-plugins': {},
});

runCommand('build', '--once', 'my-plugin@frontend,my-theme');

expect(mockWebpack).toHaveBeenCalled();
expect(process.stdout.write).toHaveBeenCalledWith(
`\x1b[1mCompiling \x1b[4mlist\x1b[0m\x1b[1m of projects in development mode.\x1b[0m\n`,
);
expect(process.stdout.write).toHaveBeenCalledWith('Processing the following projects:\n');
expect(process.stdout.write).toHaveBeenCalledWith(` * my-plugin `);
expect(process.stdout.write).toHaveBeenCalledWith(` * my-theme `);
});

it('runs specific projects mode if some requested projects are not found', () => {
mockVol.fromNestedJSON({
plugins: {
Expand Down
14 changes: 14 additions & 0 deletions docs/CLI-Command-Usage.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,6 +33,20 @@ build-tools build my-plugin,my-theme

Notice that each defined project is not a full path, nor an entry point. We use the directory name as the project and the build tools then look for those as defined in the [Structuring Your Project guide](https://github.com/bigbite/build-tools/wiki/Project-Structuring), seeking through `client-mu-plugins`,`plugins` and `themes`.

## Individual entrypoints
You can define specific entrypoints to build by specifying them after `@` symbol and seperating entrypoints by `+` symbol.

Build all frontend entrypoints
```bash
build-tools build @frontend+editor
```

Build single project entrypoitns
```bash
build-tools build my-plugin@frontend
```


## Site-wide
If you need to build an entire sites worth of projects, which will often be the case come deployment, you can build all applicable projects by running the command from within your `wp-content` directory.

Expand Down
18 changes: 15 additions & 3 deletions src/commands/build.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ const spinner = ora();

const { findAllProjectPaths, validateProject } = require('./../utils/projectpaths');
const { getPackage } = require('./../utils/get-package');
const { getFilteredEntryPoints } = require('./../utils/get-filtered-entrypoints');
const dirsExist = require('../utils/dirs-exist');
const getProjectConfig = require('../utils/get-project-config');

Expand Down Expand Up @@ -59,9 +60,9 @@ 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 projectsList = projects.split(',').map((item) => item.split('@')[0]).filter((item) => item.length > 0);
const hasTargetDirs = dirsExist(targetDirs);
const isAllProjects = (site || hasTargetDirs) && !projects;
const isAllProjects = (site || hasTargetDirs) && (!projects || projectsList.length === 0);

let packages = [];

Expand Down Expand Up @@ -115,7 +116,18 @@ exports.handler = async ({
spinner.start('Building webpack configs.\n');

const configMap = validProjects.map((packageObject) => {
const projectConfig = getProjectConfig(packageObject, mode);
// Empty array means all entrypoints.
let filteredEntrypoints = [];

if (projects.startsWith('@')) {
// Handle entrypoints when for standalone builds and all project builds.
filteredEntrypoints = projects.split('@')[1].split('+');
} else {
// Handle entrypoints for each specified project build.
filteredEntrypoints = getFilteredEntryPoints(projects)[packageObject.name];
}

const projectConfig = getProjectConfig(packageObject, mode, filteredEntrypoints);

return webpackConfig(projectConfig, mode);
});
Expand Down
2 changes: 1 addition & 1 deletion src/commands/build/webpack.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ module.exports = (__PROJECT_CONFIG__, mode) => {

let webpackConfig = {
mode,
entry: entrypoints(__PROJECT_CONFIG__.paths.src),
entry: entrypoints(__PROJECT_CONFIG__.paths.src, __PROJECT_CONFIG__.filteredEntrypoints),

resolve: {
modules: [__PROJECT_CONFIG__.paths.node_modules, 'node_modules'],
Expand Down
2 changes: 1 addition & 1 deletion src/utils/__tests__/entrypoints.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ describe('Entrypoints', () => {
},
});
const src = './';
const result = entrypoints(src);
const result = entrypoints(src, []);
const cwd = process.cwd();
expect(result).toEqual({
'empty-dir': `${cwd}/entrypoints/empty-dir`,
Expand Down
21 changes: 21 additions & 0 deletions src/utils/__tests__/get-filtered-entrypoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
const { getFilteredEntryPoints } = require('../get-filtered-entrypoints');

describe('Get filtered entrypoints', () => {
it('Return object with empty array if no filtered entry points', () => {
const projects = 'test-project';
const result = getFilteredEntryPoints(projects);
expect(result).toEqual({ 'test-project': [] });
});

it('Returns object with array of entrypoints', () => {
const projects = 'test-project@frontend+editor';
const result = getFilteredEntryPoints(projects);
expect(result).toEqual({ 'test-project': ['frontend', 'editor'] });
});

it('Return object with entrypoints where @ is present and empty array if non defined', () => {
const projects = 'test-project@editor,test-project-2';
const result = getFilteredEntryPoints(projects);
expect(result).toEqual({ 'test-project': ['editor'], 'test-project-2': []});
});
});
24 changes: 17 additions & 7 deletions src/utils/entrypoints.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,10 @@ const path = require('path');
* Create entry points object from src folder.
*
* @param {string} src Project src path
* @param {array} filteredEntrypoints entry point to build (ie frontend, editor, etc).
* @returns {object} webpack entrypoints
*/
module.exports = (src) => {
module.exports = (src, filteredEntrypoints) => {
const entrypoints = `${src}/entrypoints`;
const pathToEntryPoints = path.resolve(process.cwd(), entrypoints);

Expand All @@ -16,10 +17,19 @@ module.exports = (src) => {
}

return fs.readdirSync(pathToEntryPoints).reduce(
(accumulator, file) => ({
...accumulator,
[file.split('.')[0]]: path.resolve(pathToEntryPoints, file),
}),
{},
);
(accumulator, file) => {
const type = file.split('.')[0];

// If types are provided, only watch/build those.
if (filteredEntrypoints.length > 0) {
if (!filteredEntrypoints.includes(type)) {
return accumulator;
}
}

return {
...accumulator,
[type]: path.resolve(pathToEntryPoints, file),
};
}, {});
};
37 changes: 37 additions & 0 deletions src/utils/get-filtered-entrypoints.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
/**
* Get filtered entrypoints for projects.
*
* Example command: build project1@entry1+entry2,project2@entry3
* Example input: 'project1@entry1+entry2,project2@entry3'
* Example return: { project1: ['entry1', 'entry2'], project2: ['entry3'] }
*
* @param {Array} projects Projects of which to filter entrypoints for.
* @returns {object} An object with project keys of which value for each is the entrypoints to build.
*/
const getFilteredEntryPoints = (projects) => {
const filteredProjectEntryPoints = {};
const projectNames = projects.split(',');

projectNames.forEach((project) => {
const projectParts = project.split('@');
const projectName = projectParts[0];
const entryPoints = projectParts[1] ? projectParts[1].split('+') : [];

if (!projectName) {
return filteredProjectEntryPoints;
}

if (entryPoints.length === 0) {
filteredProjectEntryPoints[projectName] = [];
return;
}

return filteredProjectEntryPoints[projectName] = entryPoints;
});

return filteredProjectEntryPoints;
};

module.exports = {
getFilteredEntryPoints,
};
4 changes: 3 additions & 1 deletion src/utils/get-project-config.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,8 @@ module.exports = (
},
path: './'
},
mode = 'development'
mode = 'development',
filteredEntrypoints = []
) => {
return {
name: packageObject?.name ?? '',
Expand All @@ -33,5 +34,6 @@ module.exports = (
clean: true,
copy: true,
mode,
filteredEntrypoints,
};
}

0 comments on commit 6830250

Please sign in to comment.