Skip to content

Commit

Permalink
Merge 4c2c5ef into 18a7c0a
Browse files Browse the repository at this point in the history
  • Loading branch information
homer0 committed Sep 2, 2019
2 parents 18a7c0a + 4c2c5ef commit 972524d
Show file tree
Hide file tree
Showing 8 changed files with 483 additions and 247 deletions.
12 changes: 11 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -105,8 +105,11 @@ All the configurations receive a single object parameter with the following prop
- `input`: The path to the target entry file
- `output`: The Rollup output settings for the target.
- `paths`: A dictionary with the filenames formats and paths of the different files the bundle can generate (`js`, `css`, `images` and `fonts`).
- `definitions`: A dictionary of defined variables that will be replaced on the bundled code.
- `definitions`: A function that generates a dictionary of variables that will be replaced on the bundled code.
- `buildType`: The indented build type (`development` or `production`).
- `copy`: A list of information for files that need to be copied during the bundling process.
- `additionalWatch`: A list of additional paths Rollup should watch for in order to restart the bundle.
- `analyze`: A flag to detect if the bundled should be analyzed or not.

#### Plugins configuration

Expand Down Expand Up @@ -201,6 +204,13 @@ That change will only be applied when building the target `myApp` on a productio

If you want to write a plugin that works with this one (like a framework plugin), there are a lot of reducer events you can listen for and use to modify the Rollup configuration:

### Configuration parameters

- Name: `rollup-configuration-parameters`
- Reduces: The parameters used by the plugin services to build a target configuration.

This is called before generating any configuration.

### Node target configuration

- Name: `rollup-node-configuration`
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
"name": "projext-plugin-rollup",
"description": "Allows projext to use Rollup as a build engine.",
"homepage": "https://homer0.github.io/projext-plugin-rollup/",
"version": "5.0.0",
"version": "5.1.0",
"repository": "homer0/projext-plugin-rollup",
"author": "Leonardo Apiwan (@homer0) <me@homer0.com>",
"license": "MIT",
Expand Down
101 changes: 56 additions & 45 deletions src/plugins/stylesheetAssets/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -341,52 +341,63 @@ class ProjextRollupStylesheetAssetsPlugin {
* @ignore
*/
_updateCSSBlock(block) {
// Get all the linked files on the block.
const paths = this._getPathsForCSSBlock(block);
let { css } = block;
// Loop all the files.
paths
// Filter those which absolute path couldn't be found.
.filter((pathChange) => !!pathChange.absPath)
// Loop the filtered list.
.forEach((pathChange) => {
const {
absPath,
line,
query,
info,
} = pathChange;
// Try to find a URL setting which filter matches a file absolute path.
const settings = this._options.urls.find((setting) => setting.filter(absPath));
// If a URL setting was found...
if (settings) {
// Generate the output path where the file will be copied.
const output = ProjextRollupUtils.formatPlaceholder(settings.output, info);
// Get the directory where the file will be copied.
const outputDir = path.dirname(output);
// Generate the new URL for the file.
const urlBase = ProjextRollupUtils.formatPlaceholder(settings.url, info);
// Append any existing query the file originally had.
const newURL = `${urlBase}${query}`;
// Generate the new statement for the CSS.
const newLine = `url('${newURL}')`;
// Generate a RegExp that matches the old statement.
const lineRegex = new RegExp(ProjextRollupUtils.escapeRegex(line.trim()), 'ig');
// if the directory wasn't already created, create it.
if (!this._createdDirectoriesCache.includes(outputDir)) {
fs.ensureDirSync(outputDir);
this._createdDirectoriesCache.push(outputDir);
let result;
/**
* If there's a map on the block (because on watch mode Rollup caches the files'
* transformations), then do the processing, otherwise, just set to return the same block.
*/
if (block.map) {
// Get all the linked files on the block.
const paths = this._getPathsForCSSBlock(block);
let { css } = block;
// Loop all the files.
paths
// Filter those which absolute path couldn't be found.
.filter((pathChange) => !!pathChange.absPath)
// Loop the filtered list.
.forEach((pathChange) => {
const {
absPath,
line,
query,
info,
} = pathChange;
// Try to find a URL setting which filter matches a file absolute path.
const settings = this._options.urls.find((setting) => setting.filter(absPath));
// If a URL setting was found...
if (settings) {
// Generate the output path where the file will be copied.
const output = ProjextRollupUtils.formatPlaceholder(settings.output, info);
// Get the directory where the file will be copied.
const outputDir = path.dirname(output);
// Generate the new URL for the file.
const urlBase = ProjextRollupUtils.formatPlaceholder(settings.url, info);
// Append any existing query the file originally had.
const newURL = `${urlBase}${query}`;
// Generate the new statement for the CSS.
const newLine = `url('${newURL}')`;
// Generate a RegExp that matches the old statement.
const lineRegex = new RegExp(ProjextRollupUtils.escapeRegex(line.trim()), 'ig');
// if the directory wasn't already created, create it.
if (!this._createdDirectoriesCache.includes(outputDir)) {
fs.ensureDirSync(outputDir);
this._createdDirectoriesCache.push(outputDir);
}
// Copy the file.
fs.copySync(absPath, output);
// Add an stats entry that the file was copied.
this._options.stats(this.name, output);
// Replace the old statement with the new one.
css = css.replace(lineRegex, newLine);
}
// Copy the file.
fs.copySync(absPath, output);
// Add an stats entry that the file was copied.
this._options.stats(this.name, output);
// Replace the old statement with the new one.
css = css.replace(lineRegex, newLine);
}
});
// Return the updated block with the new CSS code.
return Object.assign({}, block, { css });
});
// set to return the updated block with the new CSS code.
result = Object.assign({}, block, { css });
} else {
result = block;
}

return result;
}
/**
* Gets a list of dictionaries with the information of all the files linked on a CSS block.
Expand Down
105 changes: 75 additions & 30 deletions src/services/building/configuration.js
Original file line number Diff line number Diff line change
Expand Up @@ -50,11 +50,17 @@ class RollupConfiguration {
}
/**
* This method generates a complete Rollup configuration for a target.
* Before creating the configuration, it uses the reducer event
* `rollup-configuration-parameters-for-browser` or `rollup-configuration-parameters-for-node`,
* depending on the target type, and then `rollup-configuration-parameters` to reduce
* the parameters ({@link RollupConfigurationParams}) the services will use to generate the
* configuration. The event recevies the parameters and expects updated parameters in return.
* @param {Target} target The target information.
* @param {string} buildType The intended build type: `production` or `development`.
* @return {Object}
* @throws {Error} If there's no base configuration for the target type.
* @throws {Error} If there's no base configuration for the target type and build type.
* @todo Stop using `events` from `targets` and inject it directly on the class.
*/
getConfig(target, buildType) {
const targetType = target.type;
Expand All @@ -64,39 +70,14 @@ class RollupConfiguration {
throw new Error(`There's no configuration for the selected build type: ${buildType}`);
}

const paths = Object.assign({}, target.output[buildType]);

const input = path.join(target.paths.source, target.entry[buildType]);

const defaultFormat = this._getTargetDefaultFormat(target);
const output = {
sourcemap: !!(target.sourceMap && target.sourceMap[buildType]),
name: target.name.replace(/-(\w)/ig, (match, letter) => letter.toUpperCase()),
};

if (target.library) {
output.format = this._getLibraryFormat(target.libraryOptions);
output.exports = 'named';
} else {
output.format = defaultFormat;
}

const filepath = `./${target.folders.build}/${paths.js}`;

const paths = Object.assign({}, target.output[buildType]);
if (paths.jsChunks === true) {
paths.jsChunks = this._generateChunkName(paths.js);
}

if (paths.jsChunks) {
output.chunkFileNames = path.basename(paths.jsChunks);
output.entryFileNames = path.basename(paths.js);
output.dir = path.dirname(filepath);
if (target.is.browser && !target.library) {
output.format = 'es';
}
} else {
output.file = filepath;
}
const output = this._getTargetOutput(target, paths, buildType);

const copy = [];
if (target.is.browser || target.bundle) {
Expand All @@ -106,7 +87,7 @@ class RollupConfiguration {
const definitions = this._getDefinitionsGenerator(target, buildType);
const additionalWatch = this._getBrowserTargetConfigurationDefinitions(target).files;

const params = {
let params = {
input,
output,
target,
Expand All @@ -123,6 +104,15 @@ class RollupConfiguration {
analyze: !!target.analyze,
};

const eventName = target.is.node ?
'rollup-configuration-parameters-for-node' :
'rollup-configuration-parameters-for-browser';

params = this.targets.events.reduce(
[eventName, 'rollup-configuration-parameters'],
params
);

let config = this.targetConfiguration(
`rollup/${target.name}.config.js`,
this.rollupConfigurations[targetType][buildType]
Expand All @@ -134,6 +124,61 @@ class RollupConfiguration {

return config;
}
/**
* Generates the Rollup output configuration setting based on the target information, its
* pared paths and the type of build.
* @param {Target} target The target information.
* @param {Object} formattedPaths The target `paths` setting for the selected build type. The
* reason they are received as a separated parameter is because
* in case the paths originally had a `jsChunk` property, the
* service parsed it in order to inject the paths to the
* actual chunks. Check the method `getConfig` for more
* information.
* @param {string} buildType The intended build type: `production` or `development`.
* @return {Object} The Rollup output configuration.
* @property {boolean} sourcemap Whether or not to include source maps.
* @property {string} name The name of the bundle, in case it exports something.
* @property {string} format The bundle format (`es`, `iifee` or `cjs`).
* @property {string} file The name of the bundle when code splitting is not used.
* @property {?string} exports In case the target is a library, this will `named`, as the
* default export mode for libraries.
* @property {?string} chunkFileNames If code splitting is used, this will be the base name of
* the chunk files.
* @property {?string} entryFileNames If code splitting is used, this will be the base name of
* the main bundle.
* @property {?string} dir If code splitting is used, this will be the directory
* where the chunk files will be saved.
* @access protected
* @ignore
*/
_getTargetOutput(target, formattedPaths, buildType) {
const output = {
sourcemap: !!(target.sourceMap && target.sourceMap[buildType]),
name: target.name.replace(/-(\w)/ig, (match, letter) => letter.toUpperCase()),
};

if (target.library) {
output.format = this._getLibraryFormat(target.libraryOptions);
output.exports = 'named';
} else {
output.format = this._getTargetDefaultFormat(target);
}

const filepath = `./${target.folders.build}/${formattedPaths.js}`;

if (formattedPaths.jsChunks) {
output.chunkFileNames = path.basename(formattedPaths.jsChunks);
output.entryFileNames = path.basename(formattedPaths.js);
output.dir = path.dirname(filepath);
if (target.is.browser && !target.library) {
output.format = 'es';
}
} else {
output.file = filepath;
}

return output;
}
/**
* Based on the taget type, this method will decide which will be the default output format
* the target will use. The reason this is the "default" format, it's because the service
Expand Down Expand Up @@ -245,8 +290,8 @@ class RollupConfiguration {
return result;
}
/**
* This is a small helper function that parses the default path of the JS file webpack will
* emmit and adds a `[name]` placeholder for webpack to replace with the chunk name.
* This is a small helper function that parses the default path of the JS file Rollup will
* emmit and adds a `[name]` placeholder for Rollup to replace with the chunk name.
* @param {string} jsPath The original path for the JS file.
* @return {string}
* @access protected
Expand Down
6 changes: 3 additions & 3 deletions src/services/configurations/pluginsConfiguration.js
Original file line number Diff line number Diff line change
Expand Up @@ -731,9 +731,9 @@ class RollupPluginSettingsConfiguration extends ConfigurationFile {
);
}
/**
* Defines the settings for the `stylesheetAssets` plugin, which is a projext's plugin that
* transform files matching a filter by copying its contents into a new file on the output
* directory and replacing its default export with a URL for them.
* Defines the settings for the `urls` plugin, which is a projext's plugin that transforms
* files matching a filter and copies its contents into a new file on the output directory,
* then it replaces its default export with a URL for them.
* This method uses the reducer event `rollup-urls-plugin-settings-configuration-for-browser` or
* `rollup-urls-plugin-settings-configuration-for-node`, depending on the target type, and then
* `rollup-urls-plugin-settings-configuration`. The event receives the settings, the `params`
Expand Down
2 changes: 1 addition & 1 deletion src/typedef.js
Original file line number Diff line number Diff line change
Expand Up @@ -555,7 +555,7 @@
* A list of {@link TargetExtraFile} with the information of files that need to be copied during
* the bundling process.
* @property {Array} additionalWatch
* A list of additional paths webpack should watch for in order to restart the bundle.
* A list of additional paths Rollup should watch for in order to restart the bundle.
*/

/**
Expand Down
53 changes: 53 additions & 0 deletions tests/plugins/stylesheetAssets/index.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -431,6 +431,59 @@ describe('plugins:stylesheetAssets', () => {
);
});

it('shouldn\'t fail to process a file without a map', () => {
// Given
const extension = '.png';
const assetOne = `file${extension}`;
const assetTwo = `fileB${extension}`;
const assetThree = 'fileC.html';
const assetFour = `fileD${extension}`;
const firstCodePart = [
`a { background: url('./${assetOne}'); }`,
`a.blue { background: url('./${assetTwo}?v=1.0'); }`,
`a.light-blue { background: url('./${assetTwo}?v=1.0'); }`,
`a.green { background: url('./${assetThree}'); }`,
]
.join('\n');
const fileContents = [
`${firstCodePart}`,
`a.red { background: url('./${assetFour}'); }`,
]
.join('\n');
const filter = jest.fn((filepath) => filepath.endsWith(extension));
rollupUtils.createFilter.mockImplementationOnce(() => filter);
ProjextRollupUtils.escapeRegex
.mockImplementation((exp) => exp.replace(/[-[\]{}()*+?.,\\^$|#\s]/g, '\\$&'));
fs.pathExistsSync.mockImplementationOnce(() => true);
fs.readFileSync.mockImplementationOnce(() => fileContents);
const url = {
include: ['include-some-files'],
exclude: ['exclude-some-files'],
output: 'output',
url: 'url',
};
const options = {
stylesheet: 'some-file.css',
urls: [url],
stats: jest.fn(),
};
let sut = null;
// When
sut = new ProjextRollupStylesheetAssetsPlugin(options);
sut.writeBundle();
// Then
expect(fs.pathExistsSync).toHaveBeenCalledTimes(1);
expect(fs.pathExistsSync).toHaveBeenCalledWith(options.stylesheet);
expect(fs.readFileSync).toHaveBeenCalledTimes(1);
expect(fs.readFileSync).toHaveBeenCalledWith(options.stylesheet, 'utf-8');
expect(ProjextRollupUtils.formatPlaceholder).toHaveBeenCalledTimes(0);
expect(fs.writeFileSync).toHaveBeenCalledTimes(1);
expect(fs.writeFileSync).toHaveBeenCalledWith(
options.stylesheet,
fileContents
);
});

it('should provide a shorthand method to instantiate the plugin', () => {
// Given
const filter = 'filter';
Expand Down

0 comments on commit 972524d

Please sign in to comment.