Skip to content

Commit

Permalink
refactor: code
Browse files Browse the repository at this point in the history
  • Loading branch information
evilebottnawi authored Oct 13, 2020
2 parents f17f368 + f45bfba commit 89cdbb1
Show file tree
Hide file tree
Showing 6 changed files with 567 additions and 530 deletions.
2 changes: 1 addition & 1 deletion .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -7,5 +7,5 @@
"node": true,
"jest": true
},
"extends": "eslint:recommended"
"extends": ["eslint:recommended", "prettier"]
}
18 changes: 18 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,24 @@
All notable changes to this project will be documented in this file.
This project adheres to [Semantic Versioning](http://semver.org/).

## [3.0.0-rc.3](https://github.com/postcss-modules-local-by-default/compare/v3.0.0-rc.2...v3.0.0-rc.3) - 2020-11-08

### Fixes

- broken release

## [3.0.0-rc.2](https://github.com/postcss-modules-local-by-default/compare/v3.0.0-rc.1...v3.0.0-rc.2) - 2020-10-08

### BREAKING CHANGE

- minimum supported `postcss` version is `^8.1.0`

### Fixes

- minimum supported `Node.js` version is `^10 || ^12 || >= 14`
- compatibility with other plugins
- compatibility with PostCSS 8

## [3.0.0-rc.1](https://github.com/postcss-modules-local-by-default/compare/v3.0.0-rc.0...v3.0.0-rc.1) - 2020-09-18

### Fixes
Expand Down
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
{
"name": "postcss-modules-extract-imports",
"version": "3.0.0-rc.1",
"version": "3.0.0-rc.3",
"description": "A CSS Modules transform to extract local aliases for inline imports",
"main": "src/index.js",
"engines": {
"node": ">= 10.13.0 || >= 12.13.0 || >= 14"
"node": "^10 || ^12 || >= 14"
},
"files": [
"src"
Expand Down Expand Up @@ -37,14 +37,15 @@
"homepage": "https://github.com/css-modules/postcss-modules-extract-imports",
"devDependencies": {
"coveralls": "^3.1.0",
"eslint": "^7.9.0",
"eslint": "^7.10.0",
"eslint-config-prettier": "^6.12.0",
"husky": "^4.3.0",
"jest": "^26.4.2",
"jest": "^26.5.2",
"lint-staged": "^10.4.0",
"postcss": "^8.0.7",
"postcss": "^8.1.1",
"prettier": "^2.1.2"
},
"peerDependencies": {
"postcss": "^8.0.0"
"postcss": "^8.1.0"
}
}
238 changes: 122 additions & 116 deletions src/index.js
Original file line number Diff line number Diff line change
@@ -1,20 +1,10 @@
const topologicalSort = require("./topologicalSort");

const declWhitelist = ["composes"];
const declFilter = new RegExp(`^(${declWhitelist.join("|")})$`);
const matchImports = /^(.+?)\s+from\s+(?:"([^"]+)"|'([^']+)'|(global))$/;
const icssImport = /^:import\((?:"([^"]+)"|'([^']+)')\)/;

const VISITED_MARKER = 1;

function createParentName(rule, root) {
return `__${root.index(rule.parent)}_${rule.selector}`;
}

function serializeImports(imports) {
return imports.map((importPath) => "`" + importPath + "`").join(", ");
}

/**
* :import('G') {}
*
Expand Down Expand Up @@ -54,139 +44,155 @@ function addImportToGraph(importId, parentId, graph, visited) {
}

visited[visitedId] = VISITED_MARKER;

siblings.push(importId);
}
}

module.exports = (options = {}) => {
let importIndex = 0;
const createImportedName =
typeof options.createImportedName !== "function"
? (importName /*, path*/) =>
`i__imported_${importName.replace(/\W/g, "_")}_${importIndex++}`
: options.createImportedName;
const failOnWrongOrder = options.failOnWrongOrder;

return {
postcssPlugin: "postcss-modules-extract-imports",
RootExit(root, postcss) {
prepare() {
const graph = {};
const visited = {};

const existingImports = {};
const importDecls = {};
const imports = {};

let importIndex = 0;

const createImportedName =
typeof options.createImportedName !== "function"
? (importName /*, path*/) =>
`i__imported_${importName.replace(/\W/g, "_")}_${importIndex++}`
: options.createImportedName;

// Check the existing imports order and save refs
root.walkRules((rule) => {
const matches = icssImport.exec(rule.selector);

if (matches) {
const [, /*match*/ doubleQuotePath, singleQuotePath] = matches;
const importPath = doubleQuotePath || singleQuotePath;

addImportToGraph(importPath, "root", graph, visited);

existingImports[importPath] = rule;
}
});

// Find any declaration that supports imports
root.walkDecls(declFilter, (decl) => {
let matches = decl.value.match(matchImports);
let tmpSymbols;

if (matches) {
let [
,
/*match*/ symbols,
doubleQuotePath,
singleQuotePath,
global,
] = matches;

if (global) {
// Composing globals simply means changing these classes to wrap them in global(name)
tmpSymbols = symbols.split(/\s+/).map((s) => `global(${s})`);
} else {
const importPath = doubleQuotePath || singleQuotePath;
const parentRule = createParentName(decl.parent, root);

addImportToGraph(importPath, parentRule, graph, visited);

importDecls[importPath] = decl;
imports[importPath] = imports[importPath] || {};

tmpSymbols = symbols.split(/\s+/).map((s) => {
if (!imports[importPath][s]) {
imports[importPath][s] = createImportedName(s, importPath);
}
return {
OnceExit(root, postcss) {
// Check the existing imports order and save refs
root.walkRules((rule) => {
const matches = icssImport.exec(rule.selector);

return imports[importPath][s];
});
}
if (matches) {
const [, /*match*/ doubleQuotePath, singleQuotePath] = matches;
const importPath = doubleQuotePath || singleQuotePath;

addImportToGraph(importPath, "root", graph, visited);

decl.value = tmpSymbols.join(" ");
}
});
existingImports[importPath] = rule;
}
});

const importsOrder = topologicalSort(graph, failOnWrongOrder);
root.walkDecls(/^composes$/, (declaration) => {
const matches = declaration.value.match(matchImports);

if (!matches) {
return;
}

let tmpSymbols;
let [
,
/*match*/ symbols,
doubleQuotePath,
singleQuotePath,
global,
] = matches;

if (global) {
// Composing globals simply means changing these classes to wrap them in global(name)
tmpSymbols = symbols.split(/\s+/).map((s) => `global(${s})`);
} else {
const importPath = doubleQuotePath || singleQuotePath;

let parent = declaration.parent;
let parentIndexes = "";

while (parent.type !== "root") {
parentIndexes =
parent.parent.index(parent) + "_" + parentIndexes;
parent = parent.parent;
}

if (importsOrder instanceof Error) {
const importPath = importsOrder.nodes.find((importPath) =>
// eslint-disable-next-line no-prototype-builtins
importDecls.hasOwnProperty(importPath)
);
const decl = importDecls[importPath];
const { selector } = declaration.parent;
const parentRule = `_${parentIndexes}${selector}`;

const errMsg =
"Failed to resolve order of composed modules " +
serializeImports(importsOrder.nodes) +
".";
addImportToGraph(importPath, parentRule, graph, visited);

throw decl.error(errMsg, {
plugin: "postcss-modules-extract-imports",
word: "composes",
});
}
importDecls[importPath] = declaration;
imports[importPath] = imports[importPath] || {};

let lastImportRule;
tmpSymbols = symbols.split(/\s+/).map((s) => {
if (!imports[importPath][s]) {
imports[importPath][s] = createImportedName(s, importPath);
}

importsOrder.forEach((path) => {
const importedSymbols = imports[path];
let rule = existingImports[path];
return imports[importPath][s];
});
}

if (!rule && importedSymbols) {
rule = postcss.rule({
selector: `:import("${path}")`,
raws: { after: "\n" },
declaration.value = tmpSymbols.join(" ");
});

if (lastImportRule) {
root.insertAfter(lastImportRule, rule);
} else {
root.prepend(rule);
const importsOrder = topologicalSort(graph, failOnWrongOrder);

if (importsOrder instanceof Error) {
const importPath = importsOrder.nodes.find((importPath) =>
// eslint-disable-next-line no-prototype-builtins
importDecls.hasOwnProperty(importPath)
);
const decl = importDecls[importPath];

throw decl.error(
"Failed to resolve order of composed modules " +
importsOrder.nodes
.map((importPath) => "`" + importPath + "`")
.join(", ") +
".",
{
plugin: "postcss-modules-extract-imports",
word: "composes",
}
);
}
}

lastImportRule = rule;

if (!importedSymbols) {
return;
}

Object.keys(importedSymbols).forEach((importedSymbol) => {
rule.append(
postcss.decl({
value: importedSymbol,
prop: importedSymbols[importedSymbol],
raws: { before: "\n " },
})
);
});
});

let lastImportRule;

importsOrder.forEach((path) => {
const importedSymbols = imports[path];
let rule = existingImports[path];

if (!rule && importedSymbols) {
rule = postcss.rule({
selector: `:import("${path}")`,
raws: { after: "\n" },
});

if (lastImportRule) {
root.insertAfter(lastImportRule, rule);
} else {
root.prepend(rule);
}
}

lastImportRule = rule;

if (!importedSymbols) {
return;
}

Object.keys(importedSymbols).forEach((importedSymbol) => {
rule.append(
postcss.decl({
value: importedSymbol,
prop: importedSymbols[importedSymbol],
raws: { before: "\n " },
})
);
});
});
},
};
},
};
};
Expand Down
6 changes: 3 additions & 3 deletions src/topologicalSort.js
Original file line number Diff line number Diff line change
Expand Up @@ -33,10 +33,10 @@ function walkGraph(node, graph, state, result, strict) {
const length = children.length;

for (let i = 0; i < length; ++i) {
const er = walkGraph(children[i], graph, state, result, strict);
const error = walkGraph(children[i], graph, state, result, strict);

if (er instanceof Error) {
return er;
if (error instanceof Error) {
return error;
}
}

Expand Down
Loading

0 comments on commit 89cdbb1

Please sign in to comment.