Skip to content
Open
Show file tree
Hide file tree
Changes from 6 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -13,3 +13,4 @@ node_modules
# Build files
dist
/test/**/output.*
/test/**/output/
3 changes: 2 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
"dev": "rollup -cw",
"test:nested": "cd test/nested && rm -rf output && rollup -c && cmp output/bundle.js expected/bundle.js && cmp output/bundle.css expected/bundle.css && cd ../..",
"test:simple": "cd test/simple && rm -rf output && rollup -c && cmp output/output.js expected.js && cmp output/output.css expected.css && cd ../..",
"test": "npm run test:simple && npm run test:nested",
"test:imports": "cd test/imports && rm -rf output && rollup -c && cmp output/bundle.js expected/bundle.js && cmp output/bundle.css expected/bundle.css && cd ../..",
"test": "npm run test:simple && npm run test:nested && npm run test:imports",
"lint": "prettier rollup.config.js src/**",
"prepare": "npm run build",
"prepublish": "npm run build"
Expand Down
102 changes: 70 additions & 32 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,38 +1,12 @@
import { createFilter } from '@rollup/pluginutils'

var arraysEqual = function(a, b) {
if (a.length !== b.length) return false

for (let i = a.length; i--;) {
if (a[i] !== b[i]) return false
}

return true
}

export default function css(options = {}) {
const filter = createFilter(options.include || ['**/*.css'], options.exclude)
const styles = {}
let dest = options.output
let hasChanged = false
let prevIds = []

// Get all CSS modules in the order that they were imported
const getCSSModules = (id, getModuleInfo, modules = new Set()) => {
if (modules.has(id)) {
return new Set()
}

if (filter(id)) modules.add(id)

// Recursively retrieve all of imported CSS modules
getModuleInfo(id).importedIds.forEach(importId => {
modules = new Set([].concat(Array.from(modules), Array.from(getCSSModules(importId, getModuleInfo, modules))))
});

return modules
};

return {
name: 'css',
buildStart() {
Expand All @@ -43,32 +17,44 @@ export default function css(options = {}) {
return
}

const { imports, codeWithoutImports } = splitImports(code);

// When output is disabled, the stylesheet is exported as a string
if (options.output === false) {
if (imports.length === 0) {
return {
code: `export default ['${JSON.stringify(code)}'`,
map: { mappings: '' }
}
}
const importNamed = imports.map((d, i) => `import i${i} from ${d}`).join('\n');
return {
code: 'export default ' + JSON.stringify(code),
code: `
${importNamed}
export default ${imports.map((_, i) => `i${i}`).join(' + ')} + '${JSON.stringify(codeWithoutImports)}'`,
map: { mappings: '' }
}
}

// Keep track of every stylesheet
// Check if it changed since last render
// NOTE: If we are in transform block, we can assume styles[id] !== code, right?
if (styles[id] !== code && (styles[id] || code)) {
styles[id] = code
if (styles[id] !== codeWithoutImports && (styles[id] || codeWithoutImports)) {
styles[id] = codeWithoutImports
hasChanged = true
}

return ''
// return a list of imports
return imports.map((d) => `import ${d}`).join('\n');
},
generateBundle(opts, bundle) {
const ids = []

// Determine import order of files
for (const file in bundle) {
const root = bundle[file].facadeModuleId
const modules = getCSSModules(root, this.getModuleInfo)
ids.push(...Array.from(modules))
const modules = getCSSModules(root, filter, this.getModuleInfo)
ids.push(...modules)
}

// If the files are imported in the same order and there are no changes
Expand Down Expand Up @@ -115,3 +101,55 @@ export default function css(options = {}) {
}
}
}

function arraysEqual(a, b) {
if (a.length !== b.length) return false
return a.every((ai, i) => ai === b[i]);
}

function splitImports(code) {
const imports = [];
const codeWithoutImports = code.replace(/@import\s+(.*);[\r\n]*/gm, (_, group) => {
imports.push(group.replace(/(["'])~/, '$1'));
return '';
});
return {
imports,
codeWithoutImports
};
}

// Get all CSS modules in the order that they were imported
function getCSSModules(id, filter, getModuleInfo) {
const modules = [];
const visited = new Set();

// traversal logic
// 1. mark node as visited
// 2. add to list at the end
// 3. go down with imports but in reverse order
// 4. reverse full list
// example
// root
// 1
// 11
// 12
// 2
// 21
// 22
// will result in the list: root, 2, 22, 21, 1, 12, 11
// revered: 11, 12, 1, 21, 22, 2, root
const visitModule = (id) => {
if (visited.has(id)) {
return;
}
visited.add(id);
if (filter(id)) {
modules.push(id);
}
const reverseChildren = getModuleInfo(id).importedIds.slice().reverse();
reverseChildren.forEach(visitModule);
}
visitModule(id);
return modules.reverse();
};
3 changes: 3 additions & 0 deletions test/imports/css/first.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
.first {
color: blue;
}
5 changes: 5 additions & 0 deletions test/imports/css/input.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import './second.css';

.last {
color: green;
}
5 changes: 5 additions & 0 deletions test/imports/css/second.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
@import './first.css';

.second {
color: green;
}
12 changes: 12 additions & 0 deletions test/imports/expected/bundle.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
.first {
color: blue;
}

.second {
color: green;
}

.last {
color: green;
}

1 change: 1 addition & 0 deletions test/imports/expected/bundle.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
console.log('css imported');
3 changes: 3 additions & 0 deletions test/imports/input.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './css/input.css'

console.log('css imported')
12 changes: 12 additions & 0 deletions test/imports/rollup.config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import css from '../../src/index.js'

export default {
input: 'input.js',
output: {
file: 'output/bundle.js',
format: 'esm'
},
plugins: [
css({ output: 'bundle.css' })
]
}