Skip to content

Commit 5374b10

Browse files
committed
Merge pull request #47 from css-modules/generic-names
Generic names
2 parents 63ce15c + ef58c9c commit 5374b10

26 files changed

+313
-230
lines changed

.eslintrc

+1
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
"mocha": true
66
},
77
"rules": {
8+
"key-spacing": [2, {"align": "value"}],
89
"no-use-before-define": [2, "nofunc"]
910
}
1011
}

generate-tests.js

+6-2
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@ function resolveTo() {
1111
}
1212

1313
let content =
14+
`import { dropCache } from '../utils/sugar';\n`+
1415
`import { equal } from 'assert';\n`+
1516
`import { readFileSync } from 'fs';\n`+
1617
`import { resolve } from 'path';\n`+
@@ -41,12 +42,13 @@ cases.forEach(dirname => {
4142
`\n`+
4243
` describe('${testCase.replace(/-/g, ' ')}', () => {\n`+
4344
` before(() => {\n`+
45+
` dropCache(resolve('test${sep + dirname + sep + testCase + sep}source.css'));\n`+
4446
` expectedCSS = normalize(readFileSync(resolve('test${sep + dirname + sep + testCase + sep}expected.css'), 'utf8'));\n`+
4547
` expectedTokens = JSON.parse(readFileSync(resolve('test${sep + dirname + sep + testCase + sep}expected.json'), 'utf8'));\n`+
4648
` hook({rootDir: resolve('test${sep + dirname}'), use: pipelines['${dirname}']});\n`+
4749
` });\n`+
4850
`\n`+
49-
` it('loader-core', done => {\n`+
51+
` it.skip('loader-core', done => {\n`+
5052
` const loader = new FileSystemLoader(resolve('test${sep + dirname}'), pipelines['${dirname}']);\n`+
5153
`\n`+
5254
` loader.fetch('${testCase + sep}source.css', '/')\n`+
@@ -69,12 +71,14 @@ cases.forEach(dirname => {
6971
`\n`+
7072
` describe('${testCase.replace(/-/g, ' ')}', () => {\n`+
7173
` before(() => {\n`+
74+
` dropCache(resolve('test${sep + dirname + sep + testCase + sep}source1.css'));\n`+
75+
` dropCache(resolve('test${sep + dirname + sep + testCase + sep}source2.css'));\n`+
7276
` expectedCSS = normalize(readFileSync(resolve('test${sep + dirname + sep + testCase + sep}expected.css'), 'utf8'));\n`+
7377
` expectedTokens = JSON.parse(readFileSync(resolve('test${sep + dirname + sep + testCase + sep}expected.json'), 'utf8'));\n`+
7478
` hook({rootDir: resolve('test${sep + dirname}'), use: pipelines['${dirname}']});\n`+
7579
` });\n`+
7680
`\n`+
77-
` it('loader-core', done => {\n`+
81+
` it.skip('loader-core', done => {\n`+
7882
` const loader = new FileSystemLoader(resolve('test${sep + dirname}'), pipelines['${dirname}']);\n`+
7983
`\n`+
8084
` loader.fetch('${testCase + sep}source1.css', '/').then(tokens1 => {\n`+

package.json

+5-5
Original file line numberDiff line numberDiff line change
@@ -8,13 +8,13 @@
88
},
99
"dependencies": {
1010
"debug": "^2.2.0",
11+
"generic-names": "^1.0.0-beta",
1112
"icss-replace-symbols": "^1.0.2",
12-
"lodash.assign": "^3.2.0",
13+
"lodash.foreach": "^3.0.3",
1314
"lodash.identity": "^3.0.0",
1415
"lodash.isarray": "^3.0.4",
1516
"lodash.isfunction": "^3.0.6",
16-
"lodash.isstring": "^3.0.1",
17-
"lodash.pick": "^3.1.0"
17+
"lodash.isstring": "^3.0.1"
1818
},
1919
"devDependencies": {
2020
"babel": "^5.8.20",
@@ -28,7 +28,7 @@
2828
"isparta": "^3.0.3",
2929
"lodash": "^3.10.1",
3030
"mocha": "^2.2.5",
31-
"postcss": "^5.x",
31+
"postcss": "^5.0.10",
3232
"postcss-modules-extract-imports": "^1.0.0",
3333
"postcss-modules-local-by-default": "^1.0.0",
3434
"postcss-modules-scope": "^1.0.0",
@@ -45,7 +45,6 @@
4545
"scripts": {
4646
"start": "esw -w .",
4747
"lint": "eslint .",
48-
"pretest": "npm run -s lint",
4948
"test": "mocha --compilers js:babel/register",
5049
"test:cov": "`npm bin`/babel-node `npm bin`/isparta cover --report text --report html `npm bin`/_mocha",
5150
"test:gen": "babel-node generate-tests",
@@ -71,6 +70,7 @@
7170
},
7271
"homepage": "https://github.com/css-modules/css-modules-require-hook",
7372
"pre-commit": [
73+
"lint",
7474
"test"
7575
]
7676
}

src/extractor.js

+48
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import postcss from 'postcss';
2+
import genericNames from 'generic-names';
3+
4+
import Values from 'postcss-modules-values';
5+
import LocalByDefault from 'postcss-modules-local-by-default';
6+
import ExtractImports from 'postcss-modules-extract-imports';
7+
import Scope from 'postcss-modules-scope';
8+
import Parser from './parser';
9+
10+
/**
11+
* @param {array} options.append
12+
* @param {array} options.prepend
13+
* @param {array} options.use
14+
* @param {function} options.createImportedName
15+
* @param {function|string} options.generateScopedName
16+
* @param {string} options.mode
17+
* @param {string} options.rootDir
18+
* @param {function} fetch
19+
* @return {object}
20+
*/
21+
export default function extractor({
22+
append = [],
23+
prepend = [],
24+
createImportedName,
25+
generateScopedName,
26+
mode,
27+
use,
28+
rootDir: context = process.cwd(),
29+
} = {}, fetch) {
30+
const scopedName = typeof generateScopedName !== 'function'
31+
? genericNames(generateScopedName || '[name]__[local]___[hash:base64:5]', {context})
32+
: generateScopedName;
33+
34+
const plugins = (use || [
35+
...prepend,
36+
Values,
37+
mode
38+
? new LocalByDefault({mode})
39+
: LocalByDefault,
40+
createImportedName
41+
? new ExtractImports({createImportedName})
42+
: ExtractImports,
43+
new Scope({generateScopedName: scopedName}),
44+
...append,
45+
]).concat(new Parser({fetch})); // no pushing in order to avoid the possible mutations
46+
47+
return postcss(plugins);
48+
}

src/guard.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
if (global._cssModulesPolyfill) {
2-
throw new Error('only one instance of css-modules/polyfill is allowed');
2+
throw new Error('only one instance of css-modules-require-hook is allowed');
33
}
44

55
global._cssModulesPolyfill = true;

src/index.js

+36-84
Original file line numberDiff line numberDiff line change
@@ -1,104 +1,57 @@
11
import debug from 'debug';
22
import hook from './hook';
3-
import { readFileSync } from 'fs';
4-
import { dirname, sep, relative, resolve } from 'path';
5-
import { get, removeQuotes } from './utility';
6-
import assign from 'lodash.assign';
73
import identity from 'lodash.identity';
8-
import pick from 'lodash.pick';
9-
import postcss from 'postcss';
10-
11-
import Values from 'postcss-modules-values';
12-
import ExtractImports from 'postcss-modules-extract-imports';
13-
import LocalByDefault from 'postcss-modules-local-by-default';
14-
import Scope from 'postcss-modules-scope';
15-
import Parser from './parser';
16-
17-
const debugFetch = debug('css-modules:fetch');
18-
const debugSetup = debug('css-modules:setup');
4+
import extractor from './extractor';
5+
import { readFileSync } from 'fs';
6+
import { dirname, resolve } from 'path';
7+
import { removeQuotes } from './utility';
8+
import validate from './validate';
9+
import './guard';
1910

2011
// cache
21-
let importNr = 0;
2212
let tokensByFile = {};
23-
// processing functions
13+
// global
14+
let instance = extractor({}, fetch);
15+
let processorOptions = {};
2416
let preProcess = identity;
2517
let postProcess;
26-
// defaults
27-
let lazyResultOpts = {};
28-
let plugins = [LocalByDefault, ExtractImports, Scope];
29-
let rootDir = process.cwd();
18+
19+
const debugFetch = debug('css-modules:fetch');
20+
const debugSetup = debug('css-modules:setup');
3021

3122
/**
32-
* @param {object} opts
33-
* @param {function} opts.createImportedName
34-
* @param {function} opts.generateScopedName
35-
* @param {function} opts.preprocessCss
36-
* @param {function} opts.processCss
37-
* @param {string} opts.rootDir
38-
* @param {string} opts.to
39-
* @param {array} opts.use
40-
* @param {array} opts.extensions
23+
* @param {array} options.extensions
24+
* @param {function} options.preprocessCss
25+
* @param {function} options.processCss
26+
* @param {string} options.to
27+
* @param {object} options.rest
4128
*/
42-
export default function setup(opts = {}) {
43-
debugSetup(opts);
29+
export default function setup({ extensions: extraExtensions, preprocessCss, processCss, to, ...rest } = {}) {
30+
debugSetup(arguments[0]);
31+
validate(arguments[0]);
32+
instance = extractor(rest, fetch);
33+
processorOptions = {to};
34+
preProcess = preprocessCss || identity;
35+
postProcess = processCss || null;
4436
// clearing cache
45-
importNr = 0;
4637
tokensByFile = {};
4738

48-
preProcess = get('preprocessCss', null, 'function', opts) || identity;
49-
postProcess = get('processCss', null, 'function', opts) || null;
50-
rootDir = get('rootDir', ['root', 'd'], 'string', opts) || process.cwd();
51-
// https://github.com/postcss/postcss/blob/master/docs/api.md#processorprocesscss-opts
52-
lazyResultOpts = pick(opts, ['to']);
53-
54-
const extraExtensions = get('extensions', null, 'array', opts);
5539
if (extraExtensions) {
56-
extraExtensions.forEach((extension) => {
57-
hook(filename => fetch(filename, filename), extension);
58-
});
59-
}
60-
61-
// Warning. Options, which aren't affected by plugins, should be processed above.
62-
const customPlugins = get('use', ['u'], 'array', opts);
63-
if (customPlugins) {
64-
return void (plugins = customPlugins);
40+
extraExtensions.forEach((extension) => hook(filename => fetch(filename, filename), extension));
6541
}
66-
67-
const prepend = get('prepend', null, 'array', opts) || [];
68-
const append = get('append', null, 'array', opts) || [];
69-
const mode = get('mode', null, 'string', opts);
70-
const createImportedName = get('createImportedName', null, 'function', opts);
71-
const generateScopedName = get('generateScopedName', null, 'function', opts);
72-
73-
plugins = [
74-
...prepend,
75-
Values,
76-
mode
77-
? new LocalByDefault({mode: opts.mode})
78-
: LocalByDefault,
79-
createImportedName
80-
? new ExtractImports({createImportedName: opts.createImportedName})
81-
: ExtractImports,
82-
generateScopedName
83-
? new Scope({generateScopedName: opts.generateScopedName})
84-
: Scope,
85-
...append,
86-
];
8742
}
8843

8944
/**
90-
* @param {string} _to Absolute or relative path. Also can be path to the Node.JS module.
91-
* @param {string} _from Absolute path (relative to root).
92-
* @param {string} _trace
45+
* @param {string} _to Absolute or relative path. Also can be path to the Node.JS module.
46+
* @param {string} from Absolute path.
9347
* @return {object}
9448
*/
95-
function fetch(_to, _from, _trace) {
96-
const trace = _trace || String.fromCharCode(importNr++);
97-
const newPath = removeQuotes(_to);
49+
function fetch(_to, from) {
50+
const to = removeQuotes(_to);
9851
// getting absolute path to the processing file
99-
const filename = /\w/.test(newPath[0])
100-
? require.resolve(newPath)
101-
: resolve(dirname(_from), newPath);
52+
const filename = /\w/i.test(to[0])
53+
? require.resolve(to)
54+
: resolve(dirname(from), to);
10255

10356
// checking cache
10457
let tokens = tokensByFile[filename];
@@ -108,16 +61,15 @@ function fetch(_to, _from, _trace) {
10861
}
10962

11063
debugFetch({cache: false, filename});
111-
const rootRelativePath = sep + relative(rootDir, filename);
11264
const CSSSource = preProcess(readFileSync(filename, 'utf8'), filename);
65+
// https://github.com/postcss/postcss/blob/master/docs/api.md#processorprocesscss-opts
66+
const lazyResult = instance.process(CSSSource, Object.assign(processorOptions, {from: filename}));
11367

114-
const lazyResult = postcss(plugins.concat(new Parser({ fetch, filename, trace })))
115-
.process(CSSSource, assign(lazyResultOpts, {from: rootRelativePath}));
116-
68+
// https://github.com/postcss/postcss/blob/master/docs/api.md#lazywarnings
11769
lazyResult.warnings().forEach(message => console.warn(message.text));
11870

119-
tokens = lazyResult.root.tokens;
12071
// updating cache
72+
tokens = lazyResult.root.tokens;
12173
tokensByFile[filename] = tokens;
12274

12375
if (postProcess) {

src/parser.js

+23-45
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,38 @@
11
import { plugin } from 'postcss';
2+
import forEach from 'lodash.foreach';
23
import replaceSymbols from 'icss-replace-symbols';
3-
44
const importRegexp = /^:import\((.+)\)$/;
5+
const exportRegexp = /^:export$/;
56

6-
export default plugin('parser', function parser(opts = {}) {
7-
const exportTokens = {};
8-
const translations = {};
9-
10-
const fetchImport = (importNode, relativeTo, depNr) => {
11-
const file = importNode.selector.match(importRegexp)[1];
12-
const depTrace = opts.trace + String.fromCharCode(depNr);
13-
const exports = opts.fetch(file, opts.filename, depTrace);
14-
15-
importNode.each(decl => {
16-
if (decl.type === 'decl') {
17-
translations[decl.prop] = exports[decl.value];
18-
}
19-
});
20-
21-
importNode.removeSelf();
22-
};
7+
/**
8+
* @param {function} options.fetch
9+
* @return {function}
10+
*/
11+
export default plugin('parser', function parser({ fetch } = {}) {
12+
return css => {
13+
// https://github.com/postcss/postcss/blob/master/docs/api.md#inputfile
14+
const file = css.source.input.file;
15+
const translations = {};
16+
const exportTokens = {};
2317

24-
const fetchAllImports = css => {
25-
let imports = 0;
18+
css.walkRules(importRegexp, rule => {
19+
const exports = fetch(RegExp.$1, file);
2620

27-
css.each(node => {
28-
if (node.type === 'rule' && node.selector.match(importRegexp)) {
29-
fetchImport(node, css.source.input.from, imports++);
30-
}
21+
rule.walkDecls(decl => translations[decl.prop] = exports[decl.value]);
22+
rule.remove();
3123
});
32-
};
33-
34-
const linkImportedSymbols = css => replaceSymbols(css, translations);
3524

36-
const handleExport = exportNode => {
37-
exportNode.each(decl => {
38-
if (decl.type === 'decl') {
39-
Object.keys(translations).forEach(translation => {
40-
decl.value = decl.value.replace(translation, translations[translation]);
41-
});
25+
replaceSymbols(css, translations);
4226

27+
css.walkRules(exportRegexp, rule => {
28+
rule.walkDecls(decl => {
29+
forEach(translations, (value, key) => decl.value = decl.value.replace(key, value));
4330
exportTokens[decl.prop] = decl.value;
44-
}
45-
});
31+
});
4632

47-
exportNode.removeSelf();
48-
};
49-
50-
const extractExports = css => css.each(node => {
51-
if (node.type === 'rule' && node.selector === ':export') handleExport(node);
52-
});
33+
rule.remove();
34+
});
5335

54-
return css => {
55-
fetchAllImports(css);
56-
linkImportedSymbols(css);
57-
extractExports(css);
5836
css.tokens = exportTokens;
5937
};
6038
});

0 commit comments

Comments
 (0)