Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix postcss modules composes imports #2642

Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
205 changes: 203 additions & 2 deletions packages/core/integration-tests/test/css.js
Expand Up @@ -289,9 +289,9 @@ describe('css', function() {
assert.equal(typeof output, 'function');

let value = output();
assert(/_index_[0-9a-z]+_1/.test(value));
assert(/_index_[0-9a-z]/.test(value));

let cssClass = value.match(/(_index_[0-9a-z]+_1)/)[1];
let cssClass = value.match(/(_index_[0-9a-z]+)/)[1];

let css = await fs.readFile(
path.join(__dirname, '/dist/index.css'),
Expand All @@ -313,6 +313,207 @@ describe('css', function() {
assert.equal(run1(), run2());
});

it('should support postcss composes imports', async function() {
let b = await bundle(
path.join(__dirname, '/integration/postcss-composes/index.js')
);

await assertBundleTree(b, {
name: 'index.js',
assets: ['index.js', 'composes-1.css', 'composes-2.css', 'mixins.css'],
childBundles: [
{
name: 'index.css',
assets: ['composes-1.css', 'composes-2.css', 'mixins.css'],
childBundles: []
},
{
type: 'map'
}
]
});

let output = await run(b);
assert.equal(typeof output, 'function');

let value = output();
const composes1Classes = value.composes1.split(' ');
const composes2Classes = value.composes2.split(' ');
assert(composes1Classes[0].startsWith('_composes1_'));
assert(composes1Classes[1].startsWith('_test_'));
assert(composes2Classes[0].startsWith('_composes2_'));
assert(composes2Classes[1].startsWith('_test_'));

let css = await fs.readFile(
path.join(__dirname, '/dist/index.css'),
'utf8'
);
let cssClass1 = value.composes1.match(/(_composes1_[0-9a-z]+)/)[1];
assert(css.includes(`.${cssClass1}`));
let cssClass2 = value.composes2.match(/(_composes2_[0-9a-z]+)/)[1];
assert(css.includes(`.${cssClass2}`));
});

it('should not include css twice for postcss composes imports', async function() {
let b = await bundle(
path.join(__dirname, '/integration/postcss-composes/index.js')
);

await run(b);

let css = await fs.readFile(
path.join(__dirname, '/dist/index.css'),
'utf8'
);
assert.equal(
css.indexOf('height: 100px;'),
css.lastIndexOf('height: 100px;')
);
});

it('should support postcss composes imports for sass', async function() {
let b = await bundle(
path.join(__dirname, '/integration/postcss-composes/index2.js')
);

await assertBundleTree(b, {
name: 'index2.js',
assets: ['index2.js', 'composes-3.css', 'mixins.scss'],
childBundles: [
{
name: 'index2.css',
assets: ['composes-3.css', 'mixins.scss'],
childBundles: []
},
{
type: 'map'
}
]
});

let output = await run(b);
assert.equal(typeof output, 'function');

let value = output();
const composes3Classes = value.composes3.split(' ');
assert(composes3Classes[0].startsWith('_composes3_'));
assert(composes3Classes[1].startsWith('_test_'));

let css = await fs.readFile(
path.join(__dirname, '/dist/index2.css'),
'utf8'
);
assert(css.includes('height: 200px;'));
});

it('should support postcss composes imports with custom path names', async function() {
let b = await bundle(
path.join(__dirname, '/integration/postcss-composes/index3.js')
);

await assertBundleTree(b, {
name: 'index3.js',
assets: ['index3.js', 'composes-4.css', 'mixins.css'],
childBundles: [
{
name: 'index3.css',
assets: ['composes-4.css', 'mixins.css'],
childBundles: []
},
{
type: 'map'
}
]
});

let output = await run(b);
assert.equal(typeof output, 'function');

let value = output();
const composes4Classes = value.composes4.split(' ');
assert(composes4Classes[0].startsWith('_composes4_'));
assert(composes4Classes[1].startsWith('_test_'));

let css = await fs.readFile(
path.join(__dirname, '/dist/index3.css'),
'utf8'
);
assert(css.includes('height: 100px;'));
});

it('should support deep nested postcss composes imports', async function() {
let b = await bundle(
path.join(__dirname, '/integration/postcss-composes/index4.js')
);

await assertBundleTree(b, {
name: 'index4.js',
assets: [
'index4.js',
'composes-5.css',
'mixins-intermediate.css',
'mixins.css'
],
childBundles: [
{
name: 'index4.css',
assets: ['composes-5.css', 'mixins-intermediate.css', 'mixins.css'],
childBundles: []
},
{
type: 'map'
}
]
});

let output = await run(b);
assert.equal(typeof output, 'function');

let value = output();
const composes5Classes = value.composes5.split(' ');
assert(composes5Classes[0].startsWith('_composes5_'));
assert(composes5Classes[1].startsWith('_intermediate_'));
assert(composes5Classes[2].startsWith('_test_'));

let css = await fs.readFile(
path.join(__dirname, '/dist/index4.css'),
'utf8'
);
assert(css.includes('height: 100px;'));
assert(css.includes('height: 300px;'));
assert(css.indexOf('._test_') < css.indexOf('._intermediate_'));
});

it('should support postcss composes imports for multiple selectors', async function() {
let b = await bundle(
path.join(__dirname, '/integration/postcss-composes/index5.js')
);

await assertBundleTree(b, {
name: 'index5.js',
assets: ['index5.js', 'composes-6.css', 'mixins.css'],
childBundles: [
{
name: 'index5.css',
assets: ['composes-6.css', 'mixins.css'],
childBundles: []
},
{
type: 'map'
}
]
});

let output = await run(b);
assert.equal(typeof output, 'function');

let value = output();
const composes6Classes = value.composes6.split(' ');
assert(composes6Classes[0].startsWith('_composes6_'));
assert(composes6Classes[1].startsWith('_test_'));
assert(composes6Classes[2].startsWith('_test-2_'));
});

it('should minify CSS in production mode', async function() {
let b = await bundle(
path.join(__dirname, '/integration/cssnano/index.js'),
Expand Down
@@ -0,0 +1,3 @@
{
"modules": true
}
@@ -0,0 +1,4 @@
.composes1 {
composes: test from './mixins.css';
border: 3px solid orange;
}
@@ -0,0 +1,4 @@
.composes2 {
composes: test from './mixins.css';
border: 3px solid red;
}
@@ -0,0 +1,4 @@
.composes3 {
composes: test from './mixins.scss';
border: 3px solid brown;
}
@@ -0,0 +1,4 @@
.composes4 {
composes: test from '~mixins.css';
border: 3px solid black;
}
@@ -0,0 +1,4 @@
.composes5 {
composes: intermediate from './mixins-intermediate.css';
border: 3px solid yellow;
}
@@ -0,0 +1,4 @@
.composes6 {
composes: test test-2 from './mixins.css';
border: 3px solid orangered;
}
@@ -0,0 +1,6 @@
var map1 = require('./composes-1.css');
var map2 = require('./composes-2.css');

module.exports = function () {
return Object.assign({}, map1, map2);
};
@@ -0,0 +1,5 @@
var map3 = require('./composes-3.css');

module.exports = function () {
return map3;
};
@@ -0,0 +1,5 @@
var map4 = require('./composes-4.css');

module.exports = function () {
return map4;
};
@@ -0,0 +1,5 @@
var map5 = require('./composes-5.css');

module.exports = function () {
return map5;
};
@@ -0,0 +1,5 @@
var map6 = require('./composes-6.css');

module.exports = function () {
return map6;
};
@@ -0,0 +1,4 @@
.intermediate {
composes: test from './mixins.css';
height: 300px;
}
@@ -0,0 +1,8 @@
.test {
height: 100px;
width: 100px;
}

.test-2 {
background: red;
}
@@ -0,0 +1,6 @@
$test: 200px;

.test {
height: $test;
width: $test;
}
4 changes: 2 additions & 2 deletions packages/core/integration-tests/test/less.js
Expand Up @@ -198,12 +198,12 @@ describe('less', function() {

let output = await run(b);
assert.equal(typeof output, 'function');
assert.equal(output(), '_index_ku5n8_1');
assert(output().startsWith('_index_'));

let css = await fs.readFile(
path.join(__dirname, '/dist/index.css'),
'utf8'
);
assert(css.includes('._index_ku5n8_1'));
assert(css.includes('._index_'));
});
});
4 changes: 2 additions & 2 deletions packages/core/integration-tests/test/stylus.js
Expand Up @@ -139,13 +139,13 @@ describe('stylus', function() {

let output = await run(b);
assert.equal(typeof output, 'function');
assert.equal(output(), '_index_g9mqo_1');
assert(output().startsWith('_index_'));

let css = await fs.readFile(
path.join(__dirname, '/dist/index.css'),
'utf8'
);
assert(css.includes('._index_g9mqo_1'));
assert(css.includes('._index_'));
});

it('should support requiring stylus files with glob dependencies', async function() {
Expand Down
1 change: 1 addition & 0 deletions packages/core/parcel-bundler/package.json
Expand Up @@ -41,6 +41,7 @@
"command-exists": "^1.2.6",
"commander": "^2.11.0",
"cross-spawn": "^6.0.4",
"css-modules-loader-core": "^1.1.0",
"cssnano": "^4.0.0",
"deasync": "^0.1.14",
"dotenv": "^5.0.0",
Expand Down
27 changes: 17 additions & 10 deletions packages/core/parcel-bundler/src/Asset.js
Expand Up @@ -84,16 +84,7 @@ class Asset {
this.dependencies.set(name, Object.assign({name}, opts));
}

addURLDependency(url, from = this.name, opts) {
if (!url || isURL(url)) {
return url;
}

if (typeof from === 'object') {
opts = from;
from = this.name;
}

resolveDependency(url, from = this.name) {
const parsed = URL.parse(url);
let depName;
let resolved;
Expand All @@ -110,8 +101,24 @@ class Asset {
depName = './' + path.relative(path.dirname(this.name), resolved);
}

return {depName, resolved};
}

addURLDependency(url, from = this.name, opts) {
if (!url || isURL(url)) {
return url;
}

if (typeof from === 'object') {
opts = from;
from = this.name;
}

const {depName, resolved} = this.resolveDependency(url, from);

this.addDependency(depName, Object.assign({dynamic: true, resolved}, opts));

const parsed = URL.parse(url);
parsed.pathname = this.options.parser
.getAsset(resolved, this.options)
.generateBundleName();
Expand Down