Skip to content

Commit

Permalink
feat: rollup transform dependencies support (#438)
Browse files Browse the repository at this point in the history
  • Loading branch information
tivac committed Jun 26, 2018
1 parent 4afe3af commit 7347d8f
Show file tree
Hide file tree
Showing 7 changed files with 230 additions and 142 deletions.
2 changes: 1 addition & 1 deletion .eslintignore
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,4 @@ node_modules/
parsers/
results/
specimens/
specimens/output/
output/
78 changes: 69 additions & 9 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

16 changes: 3 additions & 13 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"watch": "jest --watch"
},
"devDependencies": {
"@tivac/eslint-config": "^2.2.0",
"@tivac/eslint-config": "^2.2.1",
"browserify": "^16.2.0",
"cli-tester": "^2.0.0",
"cssnano": "^4.0.0-rc.1",
Expand All @@ -34,29 +34,19 @@
"from2-string": "^1.1.0",
"husky": "^0.14.3",
"jest": "^23.1.0",
"jest-cli": "^23.1.0",
"lerna": "^3.0.0-beta.21",
"lint-staged": "^7.0.4",
"modular-css-core": "file:./packages/core",
"pegjs": "^0.10.0",
"rollup": "^0.60.4",
"rollup": "^0.61.2",
"rollup-plugin-svelte": "^4.1.0",
"shelljs": "^0.8.1",
"svelte": "^2.8.1",
"test-utils": "file:./packages/test-utils",
"watchify": "^3.9.0",
"webpack": "^4.12.1"
},
"jest": {
"coveragePathIgnorePatterns": [
"node_modules",
"parsers",
"test-utils"
],
"watchPathIgnorePatterns": [
"test/output",
"test/specimens"
]
},
"lint-staged": {
"*.js": [
"eslint --fix",
Expand Down
2 changes: 1 addition & 1 deletion packages/rollup/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ export default {

### `common`

File name to use in case there are any CSS dependencies that appear in multiple bundles.
File name to use in case there are any CSS dependencies that appear in multiple bundles. Defaults to "common.css".

### `include`/`exclude`

Expand Down
159 changes: 91 additions & 68 deletions packages/rollup/rollup.js
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ function extensionless(file) {

module.exports = function(opts) {
const options = Object.assign(Object.create(null), {
common : false,
common : "common.css",

json : false,
map : true,
Expand All @@ -36,117 +36,140 @@ module.exports = function(opts) {

const processor = options.processor || new Processor(options);

let runs = 0;

return {
name : "modular-css",
name : "modular-css-rollup",

transform(code, id) {
let removed = [];

if(!filter(id)) {
return null;
}

// If the file is being re-processed we need to remove it to
// avoid cache staleness issues
if(runs) {
removed = processor.remove(id);
if(id in processor.files) {
processor.dependencies(id)
.concat(id)
.forEach((file) => processor.remove(file));
}

return Promise.all(
// Run current file first since it's already in-memory
[ processor.string(id, code) ].concat(
removed.map((file) =>
processor.file(file)
)
)
)
.then((results) => {
const [ result ] = results;
return processor.string(id, code).then((result) => {
const exported = output.join(result.exports);

let out = [
const out = [
`export default ${JSON.stringify(exported, null, 4)};`,
];

// Add dependencies
out = out.concat(
processor.dependencies(id).map((file) =>
`import "${slash(file)}";`
)
);

if(options.namedExports === false) {
return {
code : out.join("\n"),
map,
};
}

Object.keys(exported).forEach((ident) => {
if(keyword.isReservedWordES6(ident) || !keyword.isIdentifierNameES6(ident)) {
this.warn(`Invalid JS identifier "${ident}", unable to export`);
if(options.namedExports) {
Object.keys(exported).forEach((ident) => {
if(keyword.isReservedWordES6(ident) || !keyword.isIdentifierNameES6(ident)) {
this.warn(`Invalid JS identifier "${ident}", unable to export`);

return;
}

return;
}

out.push(`export var ${ident} = ${JSON.stringify(exported[ident])};`);
});
out.push(`export var ${ident} = ${JSON.stringify(exported[ident])};`);
});
}

const dependencies = processor.dependencies(id);

return {
code : out.join("\n"),
map,
dependencies,
};
});
},

buildEnd() {
runs++;
},
async generateBundle(outputOptions, bundles) {
const usage = new Map();
const common = new Map();
const files = [];

let to;

if(!outputOptions.file && !outputOptions.dir) {
to = path.join(process.cwd(), outputOptions.assetFileNames || "");
} else {
to = path.join(
outputOptions.dir ? outputOptions.dir : path.dirname(outputOptions.file),
outputOptions.assetFileNames
);
}

// First pass is used to calculate JS usage of CSS dependencies
Object.keys(bundles).forEach((entry) => {
const file = {
entry,
base : extensionless(entry),

async generateBundle(outputOptions, bundle) {
const bundles = [];
const common = processor.dependencies();
css : [ ],
};

Object.keys(bundle).forEach((entry) => {
const files = Object.keys(bundle[entry].modules).filter(filter);
// Get CSS files being used by each entry point
const css = Object.keys(bundles[entry].modules).filter(filter);

if(!files.length) {
if(!css.length) {
return;
}

// remove the files being exported from the common bundle
files.forEach((file) =>
common.splice(common.indexOf(file), 1)
);
// Get dependency chains for each file
css.forEach((start) => {
const used = processor.dependencies(start).concat(start);

file.css = file.css.concat(used);

bundles.push({
entry,
files,
base : extensionless(entry),
used.forEach((dep) => {
usage.set(dep, usage.has(dep) ? usage.get(dep) + 1 : 1);
});
});

files.push(file);
});

// Common chunk only emitted if configured & if necessary
if(options.common && common.length) {
bundles.push({
// Second pass removes any dependencies appearing in multiple bundles
files.forEach((file) => {
const { css } = file;

file.css = css.filter((dep) => {
if(usage.get(dep) > 1) {
common.set(dep, true);

return false;
}

return true;
});
});

// Add any other files that weren't part of a bundle to the common chunk
Object.keys(processor.files).forEach((file) => {
if(!usage.has(file)) {
common.set(file, true);
}
});

// Common chunk only emitted if necessary
if(common.size) {
files.push({
entry : options.common,
base : extensionless(options.common),
files : common,
css : [ ...common.keys() ],
});
}

await Promise.all(
bundles.map(async ({ base, files }) => {
const css = this.emitAsset(`${base}.css`);

files
.filter(({ css }) => css.length)
.map(async ({ base, css }) => {
const id = this.emitAsset(`${base}.css`);

const result = await processor.output({
to : css,
files,
to,
files : css,
});

this.setAssetSource(css, result.css);
this.setAssetSource(id, result.css);

if(options.json) {
this.emitAsset(`${base}.json`, JSON.stringify(result.compositions, null, 4));
Expand Down
Loading

0 comments on commit 7347d8f

Please sign in to comment.