Skip to content

Commit

Permalink
Parcel 2: Add CSS transformer (#2440)
Browse files Browse the repository at this point in the history
  • Loading branch information
padmaia authored and devongovett committed Jan 3, 2019
1 parent f43dd68 commit 574fa44
Show file tree
Hide file tree
Showing 11 changed files with 1,536 additions and 1,017 deletions.
3 changes: 2 additions & 1 deletion packages/configs/default/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
"@parcel/transformer-babel",
"@parcel/transformer-js",
"@parcel/transformer-terser"
]
],
"*.css": ["@parcel/transformer-css"]
},
"namers": ["@parcel/namer-default"],
"packagers": {
Expand Down
1 change: 1 addition & 0 deletions packages/configs/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
"@parcel/namer-default": "^2.0.0",
"@parcel/packager-js": "^2.0.0",
"@parcel/transformer-babel": "^2.0.0",
"@parcel/transformer-css": "^2.0.0",
"@parcel/transformer-js": "^2.0.0",
"@parcel/transformer-terser": "^2.0.0"
}
Expand Down
3 changes: 2 additions & 1 deletion packages/core/types/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -163,7 +163,8 @@ export type AssetOutput = {
export type AST = {
type: string,
version: string,
program: any
program: any,
isDirty: boolean
};

export type Config = any;
Expand Down
3 changes: 3 additions & 0 deletions packages/examples/simple/src/child.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
h2 {
color: green;
}
2 changes: 2 additions & 0 deletions packages/examples/simple/src/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
import styles from './styles.css';

import('./async');
import('./async2');

Expand Down
5 changes: 5 additions & 0 deletions packages/examples/simple/src/styles.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import './child.css';

h1 {
color: gray;
}
2 changes: 1 addition & 1 deletion packages/resolvers/default/src/DefaultResolver.js
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import nodeBuiltins from 'node-libs-browser';
export default new Resolver({
async resolve(dep: Dependency, cli: CLIOptions, rootDir: string) {
const resolved = await new NodeResolver({
extensions: ['js', 'json'],
extensions: ['js', 'json', 'css'],
cli,
rootDir
}).resolve(dep);
Expand Down
6 changes: 6 additions & 0 deletions packages/transformers/css/.eslintrc.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"extends": "@parcel/eslint-config",
"parserOptions": {
"sourceType": "module"
}
}
19 changes: 19 additions & 0 deletions packages/transformers/css/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"name": "@parcel/transformer-css",
"version": "2.0.0",
"license": "MIT",
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/parcel.git"
},
"main": "src/CSSTransformer.js",
"dependencies": {
"@parcel/plugin": "2.0.0",
"postcss": "^7.0.5",
"postcss-value-parser": "^3.3.1",
"semver": "^5.4.1"
},
"devDependencies": {
"@parcel/eslint-config": "1.10.3"
}
}
135 changes: 135 additions & 0 deletions packages/transformers/css/src/CSSTransformer.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,135 @@
// @flow
import {Transformer} from '@parcel/plugin';
import postcss from 'postcss';
import valueParser from 'postcss-value-parser';
import semver from 'semver';

const URL_RE = /url\s*\("?(?![a-z]+:)/;
const IMPORT_RE = /@import/;
const PROTOCOL_RE = /^[a-z]+:/;

function canHaveDependencies(asset) {
let {filePath, code} = asset;
return !/\.css$/.test(filePath) || IMPORT_RE.test(code) || URL_RE.test(code);
}

function addURLDependency(asset, url: string, opts) {
asset.addDependency({
moduleSpecifier: url,
isAsync: true,
...opts
});
}

export default new Transformer({
canReuseAST(ast) {
return ast.type === 'postcss' && semver.satisfies(ast.version, '^7.0.0');
},

parse(asset) {
if (!canHaveDependencies(asset)) {
return null;
}

return {
type: 'postcss',
version: '7.0.0',
isDirty: false,
program: postcss.parse(asset.code, {
from: asset.filePath,
to: asset.filePath
})
};
},

transform(asset) {
if (!asset.ast) {
return [asset];
}

asset.ast.program.walkAtRules('import', rule => {
let params = valueParser(rule.params);
let [name, ...media] = params.nodes;
let moduleSpecifier;
if (
name.type === 'function' &&
name.value === 'url' &&
name.nodes.length
) {
name = name.nodes[0];
}

moduleSpecifier = name.value;

if (!moduleSpecifier) {
throw new Error('Could not find import name for ' + rule);
}

if (PROTOCOL_RE.test(moduleSpecifier)) {
return;
}

// If this came from an inline <style> tag, don't inline the imported file. Replace with the correct URL instead.
// TODO: run CSSPackager on inline style tags.
// let inlineHTML =
// this.options.rendition && this.options.rendition.inlineHTML;
// if (inlineHTML) {
// name.value = addURLDependency(asset, dep, {loc: rule.source.start});
// rule.params = params.toString();
// } else {
media = valueParser.stringify(media).trim();
let dep = {
moduleSpecifier,
media,
loc: rule.source.start
};
asset.addDependency(dep);
rule.remove();
// }

asset.ast.isDirty = true;
});

asset.ast.program.walkDecls(decl => {
if (URL_RE.test(decl.value)) {
let parsed = valueParser(decl.value);
let isDirty = false;

parsed.walk(node => {
if (
node.type === 'function' &&
node.value === 'url' &&
node.nodes.length
) {
let url = addURLDependency(asset, node.nodes[0].value, {
loc: decl.source.start
});
isDirty = node.nodes[0].value !== url;
node.nodes[0].value = url;
}
});

if (isDirty) {
decl.value = parsed.toString();
asset.ast.isDirty = true;
}
}
});

return [asset];
},

generate(asset) {
let code;
if (!asset.ast.isDirty) {
code = asset.code;
} else {
code = '';
postcss.stringify(asset.ast.program, c => (code += c));
}

return {
code
};
}
});
Loading

0 comments on commit 574fa44

Please sign in to comment.