Skip to content

Commit ae19a8d

Browse files
committed
1 parent b27907f commit ae19a8d

File tree

9 files changed

+143
-176
lines changed

9 files changed

+143
-176
lines changed

package.json

Lines changed: 9 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -7,12 +7,15 @@
77
"test": "test"
88
},
99
"dependencies": {
10-
"icss-replace-symbols": "1.0.2",
11-
"postcss": "5.2.0",
12-
"postcss-modules-values": "1.2.2",
13-
"postcss-modules-extract-imports": "1.0.0",
14-
"postcss-modules-local-by-default": "1.1.1",
15-
"postcss-modules-scope": "1.0.2"
10+
"generic-names": "^1.0.2",
11+
"icss-replace-symbols": "^1.0.2",
12+
"lodash.partialright": "^4.2.1",
13+
"postcss": "^5.2.0",
14+
"postcss-modules-extract-imports": "^1.0.0",
15+
"postcss-modules-local-by-default": "^1.1.1",
16+
"postcss-modules-parser": "^1.1.0",
17+
"postcss-modules-scope": "^1.0.2",
18+
"postcss-modules-values": "1.2.2"
1619
},
1720
"devDependencies": {
1821
"babel": "5.8.29",

src/file-system-loader.js

Lines changed: 30 additions & 44 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import Core from './index.js'
2-
import fs from 'fs'
3-
import path from 'path'
1+
import core from './index'
2+
import { readFile } from 'fs'
3+
import { dirname, resolve } from 'path'
44

55
// Sorts dependencies in the following way:
66
// AAA comes before AA and A
@@ -20,58 +20,44 @@ const traceKeySorter = ( a, b ) => {
2020
};
2121

2222
export default class FileSystemLoader {
23-
constructor( root, plugins ) {
24-
this.root = root
25-
this.sources = {}
26-
this.traces = {}
23+
24+
constructor( options, processorOptions = {} ) {
25+
this.processorOptions = processorOptions
26+
this.core = core( options, this.fetch.bind(this) )
2727
this.importNr = 0
28-
this.core = new Core(plugins)
29-
this.tokensByFile = {};
28+
this.sources = {}
29+
this.tokensByFile = {}
3030
}
3131

32-
fetch( _newPath, relativeTo, _trace ) {
33-
let newPath = _newPath.replace( /^["']|["']$/g, "" ),
34-
trace = _trace || String.fromCharCode( this.importNr++ )
35-
return new Promise( ( resolve, reject ) => {
36-
let relativeDir = path.dirname( relativeTo ),
37-
rootRelativePath = path.resolve( relativeDir, newPath ),
38-
fileRelativePath = path.resolve( path.join( this.root, relativeDir ), newPath )
32+
fetch( _to, from ) {
33+
let to = _to.replace( /^["']|["']$/g, '' ),
34+
trace = String.fromCharCode( this.importNr++ )
3935

40-
// if the path is not relative or absolute, try to resolve it in node_modules
41-
if (newPath[0] !== '.' && newPath[0] !== '/') {
42-
try {
43-
fileRelativePath = require.resolve(newPath);
36+
const filename = /\w/i.test(to[0])
37+
? require.resolve(to)
38+
: resolve(dirname(from), to)
39+
40+
return new Promise(( resolve, reject ) => {
41+
readFile( filename, 'utf8', (err, source) => {
42+
if (err) {
43+
return void reject(err);
4444
}
45-
catch (e) {}
46-
}
4745

48-
const tokens = this.tokensByFile[fileRelativePath]
49-
if (tokens) { return resolve(tokens) }
46+
this.core.process( source, Object.assign( this.processorOptions, { from: filename } ) )
47+
.then( result => {
48+
this.sources[filename] = result.css
49+
this.tokensByFile[filename] = result.root.tokens
5050

51-
fs.readFile( fileRelativePath, "utf-8", ( err, source ) => {
52-
if ( err ) reject( err )
53-
this.core.load( source, rootRelativePath, trace, this.fetch.bind( this ) )
54-
.then( ( { injectableSource, exportTokens } ) => {
55-
this.sources[fileRelativePath] = injectableSource
56-
this.traces[trace] = fileRelativePath
57-
this.tokensByFile[fileRelativePath] = exportTokens
58-
resolve( exportTokens )
59-
}, reject )
51+
resolve( this.tokensByFile[filename] )
52+
} )
53+
.catch( reject )
6054
} )
61-
} )
55+
})
6256
}
6357

6458
get finalSource() {
65-
const traces = this.traces
66-
const sources = this.sources
67-
let written = new Set()
68-
69-
return Object.keys( traces ).sort( traceKeySorter ).map(key => {
70-
const filename = traces[key]
71-
if (written.has(filename)) { return null }
72-
written.add(filename)
7359

74-
return sources[filename];
75-
}).join( "" )
60+
return Object.keys( this.sources ).sort( traceKeySorter ).map( s => this.sources[s] )
61+
.join( '' )
7662
}
7763
}

src/index.js

Lines changed: 59 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,66 @@
1-
import postcss from 'postcss'
2-
import localByDefault from 'postcss-modules-local-by-default'
3-
import extractImports from 'postcss-modules-extract-imports'
4-
import scope from 'postcss-modules-scope'
5-
import values from 'postcss-modules-values'
1+
import postcss from 'postcss';
2+
import genericNames from 'generic-names';
3+
import partialRight from 'lodash.partialright';
4+
import { relative } from 'path';
65

7-
import Parser from './parser'
6+
import LocalByDefault from 'postcss-modules-local-by-default'
7+
import ExtractImports from 'postcss-modules-extract-imports'
8+
import Scope from 'postcss-modules-scope'
9+
import Parser from 'postcss-modules-parser'
10+
import Values from 'postcss-modules-values'
811

9-
export default class Core {
10-
constructor( plugins ) {
11-
this.plugins = plugins || Core.defaultPlugins
12-
}
12+
/**
13+
* @param {array} options.append
14+
* @param {array} options.prepend
15+
* @param {array} options.use
16+
* @param {function} options.createImportedName
17+
* @param {function|string} options.generateScopedName
18+
* @param {string} options.mode
19+
* @param {string} options.rootDir
20+
* @param {function} fetch
21+
* @return {object}
22+
*/
23+
export default function core({
24+
append = [],
25+
prepend = [],
26+
createImportedName,
27+
generateScopedName: scopedName,
28+
rootDir: context = process.cwd(),
29+
mode,
30+
use,
31+
} = {}, _fetch) {
32+
let instance
33+
let generateScopedName
1334

14-
load( sourceString, sourcePath, trace, pathFetcher ) {
15-
let parser = new Parser( pathFetcher, trace )
35+
const fetch = function () {
36+
return _fetch.apply(null, Array.prototype.slice.call(arguments).concat(instance));
37+
}
1638

17-
return postcss( this.plugins.concat( [parser.plugin] ) )
18-
.process( sourceString, { from: "/" + sourcePath } )
19-
.then( result => {
20-
return { injectableSource: result.css, exportTokens: parser.exportTokens }
21-
} )
39+
if (scopedName) {
40+
// https://github.com/css-modules/postcss-modules-scope/blob/master/src/index.js#L38
41+
generateScopedName = typeof scopedName !== 'function'
42+
? genericNames(scopedName || '[name]__[local]___[hash:base64:5]', {context})
43+
: (local, filepath, css) => scopedName(local, filepath, css, context)
44+
} else {
45+
generateScopedName = (localName, filepath) => {
46+
return Scope.generateScopedName(localName, relative(context, filepath));
47+
}
2248
}
23-
}
2449

25-
// These four plugins are aliased under this package for simplicity.
26-
Core.values = values
27-
Core.localByDefault = localByDefault
28-
Core.extractImports = extractImports
29-
Core.scope = scope
50+
const plugins = (use || [
51+
...prepend,
52+
Values,
53+
mode
54+
? new LocalByDefault({mode})
55+
: LocalByDefault,
56+
createImportedName
57+
? new ExtractImports({createImportedName})
58+
: ExtractImports,
59+
new Scope({generateScopedName}),
60+
...append,
61+
])
62+
.concat(new Parser({fetch})) // no pushing in order to avoid the possible mutations
3063

31-
Core.defaultPlugins = [values, localByDefault, extractImports, scope]
64+
instance = postcss(plugins)
65+
return instance;
66+
}

src/parser.js

Lines changed: 0 additions & 63 deletions
This file was deleted.

test/test-cases.js

Lines changed: 34 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,34 @@
1-
"use strict";
2-
3-
import assert from "assert"
4-
import fs from "fs"
5-
import path from "path"
6-
import FileSystemLoader from "../src/file-system-loader"
1+
import assert from 'assert'
2+
import { existsSync, readdirSync, readFileSync } from 'fs'
3+
import { join } from 'path'
4+
import FileSystemLoader from '../src/file-system-loader'
75

86
let normalize = ( str ) => {
9-
return str.replace( /\r\n?/g, "\n" );
7+
return str.replace( /\r\n?/g, '\n' );
108
}
119

1210
const pipelines = {
13-
"test-cases": undefined,
14-
"cssi": []
11+
'test-cases': undefined,
12+
'cssi': []
1513
}
1614

1715
Object.keys( pipelines ).forEach( dirname => {
1816
describe( dirname, () => {
19-
let testDir = path.join( __dirname, dirname )
20-
fs.readdirSync( testDir ).forEach( testCase => {
21-
if ( fs.existsSync( path.join( testDir, testCase, "source.css" ) ) ) {
22-
it( "should " + testCase.replace( /-/g, " " ), done => {
23-
let expected = normalize( fs.readFileSync( path.join( testDir, testCase, "expected.css" ), "utf-8" ) )
24-
let loader = new FileSystemLoader( testDir, pipelines[dirname] )
25-
let expectedTokens = JSON.parse( fs.readFileSync( path.join( testDir, testCase, "expected.json" ), "utf-8" ) )
26-
loader.fetch( `${testCase}/source.css`, "/" ).then( tokens => {
27-
assert.equal( loader.finalSource, expected )
28-
assert.equal( JSON.stringify( tokens ), JSON.stringify( expectedTokens ) )
29-
} ).then( done, done )
17+
let testDir = join( __dirname, dirname )
18+
readdirSync( testDir ).forEach( testCase => {
19+
if ( existsSync( join( testDir, testCase, 'source.css' ) ) ) {
20+
it( 'should ' + testCase.replace( /-/g, ' ' ), done => {
21+
const loader = new FileSystemLoader({rootDir: testDir, use: pipelines[dirname]})
22+
23+
let expected = normalize( readFileSync( join( testDir, testCase, 'expected.css' ), 'utf-8' ) )
24+
let expectedTokens = JSON.parse( readFileSync( join( testDir, testCase, 'expected.json' ), 'utf-8' ) )
25+
let filepath = join(testDir, testCase, 'source.css')
26+
27+
loader.fetch(filepath, filepath, null)
28+
.then( tokens => {
29+
assert.equal( loader.finalSource, expected )
30+
assert.equal( JSON.stringify( tokens ), JSON.stringify( expectedTokens ) )
31+
} ).then( done, done )
3032
} );
3133
}
3234
} );
@@ -35,16 +37,20 @@ Object.keys( pipelines ).forEach( dirname => {
3537

3638
// special case for testing multiple sources
3739
describe( 'multiple sources', () => {
38-
let testDir = path.join( __dirname, 'test-cases' )
40+
let testDir = join( __dirname, 'test-cases' )
3941
let testCase = 'multiple-sources';
4042
let dirname = 'test-cases';
41-
if ( fs.existsSync( path.join( testDir, testCase, "source1.css" ) ) ) {
42-
it( "should " + testCase.replace( /-/g, " " ), done => {
43-
let expected = normalize( fs.readFileSync( path.join( testDir, testCase, "expected.css" ), "utf-8" ) )
44-
let loader = new FileSystemLoader( testDir, pipelines[dirname] )
45-
let expectedTokens = JSON.parse( fs.readFileSync( path.join( testDir, testCase, "expected.json" ), "utf-8" ) )
46-
loader.fetch( `${testCase}/source1.css`, "/" ).then( tokens1 => {
47-
loader.fetch( `${testCase}/source2.css`, "/" ).then( tokens2 => {
43+
if ( existsSync( join( testDir, testCase, 'source1.css' ) ) ) {
44+
it( 'should ' + testCase.replace( /-/g, ' ' ), done => {
45+
const loader = new FileSystemLoader({rootDir: testDir, use: pipelines[dirname]})
46+
47+
let expected = normalize( readFileSync( join( testDir, testCase, 'expected.css' ), 'utf-8' ) )
48+
let expectedTokens = JSON.parse( readFileSync( join( testDir, testCase, 'expected.json' ), 'utf-8' ) )
49+
let filepath1 = join(testDir, testCase, 'source1.css')
50+
let filepath2 = join(testDir, testCase, 'source2.css')
51+
52+
loader.fetch( filepath1, filepath1, null ).then( tokens1 => {
53+
loader.fetch( filepath2, filepath2, null ).then( tokens2 => {
4854
assert.equal( loader.finalSource, expected )
4955
const tokens = Object.assign({}, tokens1, tokens2);
5056
assert.equal( JSON.stringify( tokens ), JSON.stringify( expectedTokens ) )

test/test-cases/compose-node-module/expected.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
._compose_node_module_cool_styles_foo__example {
1+
._node_modules_cool_styles_foo__example {
22
color: #F00;
33
}
44
._compose_node_module_source__foo {
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
{
2-
"foo": "_compose_node_module_source__foo _compose_node_module_cool_styles_foo__example"
2+
"foo": "_compose_node_module_source__foo _node_modules_cool_styles_foo__example"
33
}

test/test-cases/multiple-dependencies/expected.css

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,3 @@
1-
._multiple_dependencies_d__d1 {
2-
color: #d1d1d1;
3-
}
4-
._multiple_dependencies_d__d2 {
5-
color: #d2d2d2;
6-
}
71
._multiple_dependencies_b__b1 {
82
color: #b1b1b1;
93
}
@@ -14,6 +8,12 @@
148
._multiple_dependencies_c__c {
159
color: #ccc;
1610
}
11+
._multiple_dependencies_d__d1 {
12+
color: #d1d1d1;
13+
}
14+
._multiple_dependencies_d__d2 {
15+
color: #d2d2d2;
16+
}
1717
._multiple_dependencies_source__a {
1818
color: #aaa;
1919
}

0 commit comments

Comments
 (0)