-
Notifications
You must be signed in to change notification settings - Fork 116
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
async/await and less logic per file (#531)
* async/await and less logic per file * Apply suggestions from code review Co-authored-by: Ryan Zimmerman <opensrc@ryanzim.com> * Apply suggestions from code review Co-authored-by: Ryan Zimmerman <opensrc@ryanzim.com> * formatting and consistency --------- Co-authored-by: Ryan Zimmerman <opensrc@ryanzim.com>
- Loading branch information
1 parent
0aaed50
commit fb2db6f
Showing
11 changed files
with
376 additions
and
366 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,113 @@ | ||
"use strict" | ||
|
||
const assignLayerNames = require("./assign-layer-names") | ||
|
||
module.exports = function applyMedia(bundle, options, state, atRule) { | ||
bundle.forEach(stmt => { | ||
if ((!stmt.media.length && !stmt.layer.length) || stmt.type === "charset") { | ||
return | ||
} | ||
|
||
if (stmt.layer.length > 1) { | ||
assignLayerNames(stmt.layer, stmt.node, state, options) | ||
} | ||
|
||
if (stmt.type === "import") { | ||
const parts = [stmt.fullUri] | ||
|
||
const media = stmt.media.join(", ") | ||
|
||
if (stmt.layer.length) { | ||
const layerName = stmt.layer.join(".") | ||
|
||
let layerParams = "layer" | ||
if (layerName) { | ||
layerParams = `layer(${layerName})` | ||
} | ||
|
||
parts.push(layerParams) | ||
} | ||
|
||
if (media) { | ||
parts.push(media) | ||
} | ||
|
||
stmt.node.params = parts.join(" ") | ||
} else if (stmt.type === "media") { | ||
if (stmt.layer.length) { | ||
const layerNode = atRule({ | ||
name: "layer", | ||
params: stmt.layer.join("."), | ||
source: stmt.node.source, | ||
}) | ||
|
||
if (stmt.parentMedia?.length) { | ||
const mediaNode = atRule({ | ||
name: "media", | ||
params: stmt.parentMedia.join(", "), | ||
source: stmt.node.source, | ||
}) | ||
|
||
mediaNode.append(layerNode) | ||
layerNode.append(stmt.node) | ||
stmt.node = mediaNode | ||
} else { | ||
layerNode.append(stmt.node) | ||
delete stmt.node | ||
stmt.nodes = [layerNode] | ||
stmt.type = "nodes" | ||
} | ||
} else { | ||
stmt.node.params = stmt.media.join(", ") | ||
} | ||
} else { | ||
const { nodes } = stmt | ||
const { parent } = nodes[0] | ||
|
||
const atRules = [] | ||
|
||
if (stmt.media.length) { | ||
const mediaNode = atRule({ | ||
name: "media", | ||
params: stmt.media.join(", "), | ||
source: parent.source, | ||
}) | ||
|
||
atRules.push(mediaNode) | ||
} | ||
|
||
if (stmt.layer.length) { | ||
const layerNode = atRule({ | ||
name: "layer", | ||
params: stmt.layer.join("."), | ||
source: parent.source, | ||
}) | ||
|
||
atRules.push(layerNode) | ||
} | ||
|
||
const outerAtRule = atRules.shift() | ||
const innerAtRule = atRules.reduce((previous, next) => { | ||
previous.append(next) | ||
return next | ||
}, outerAtRule) | ||
|
||
parent.insertBefore(nodes[0], outerAtRule) | ||
|
||
// remove nodes | ||
nodes.forEach(node => { | ||
node.parent = undefined | ||
}) | ||
|
||
// better output | ||
nodes[0].raws.before = nodes[0].raws.before || "\n" | ||
|
||
// wrap new rules with media query and/or layer at rule | ||
innerAtRule.append(nodes) | ||
|
||
stmt.type = "media" | ||
stmt.node = outerAtRule | ||
delete stmt.nodes | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,15 @@ | ||
"use strict" | ||
|
||
module.exports = function applyRaws(bundle) { | ||
bundle.forEach((stmt, index) => { | ||
if (index === 0) return | ||
|
||
if (stmt.parent) { | ||
const { before } = stmt.parent.node.raws | ||
if (stmt.type === "nodes") stmt.nodes[0].raws.before = before | ||
else stmt.node.raws.before = before | ||
} else if (stmt.type === "nodes") { | ||
stmt.nodes[0].raws.before = stmt.nodes[0].raws.before || "\n" | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,18 @@ | ||
"use strict" | ||
|
||
module.exports = function applyStyles(bundle, styles) { | ||
styles.nodes = [] | ||
|
||
// Strip additional statements. | ||
bundle.forEach(stmt => { | ||
if (["charset", "import", "media"].includes(stmt.type)) { | ||
stmt.node.parent = undefined | ||
styles.append(stmt.node) | ||
} else if (stmt.type === "nodes") { | ||
stmt.nodes.forEach(node => { | ||
node.parent = undefined | ||
styles.append(node) | ||
}) | ||
} | ||
}) | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,207 @@ | ||
"use strict" | ||
|
||
const path = require("path") | ||
|
||
const assignLayerNames = require("./assign-layer-names") | ||
const dataURL = require("./data-url") | ||
const joinLayer = require("./join-layer") | ||
const joinMedia = require("./join-media") | ||
const parseStatements = require("./parse-statements") | ||
const processContent = require("./process-content") | ||
const resolveId = require("./resolve-id") | ||
|
||
async function parseStyles( | ||
result, | ||
styles, | ||
options, | ||
state, | ||
media, | ||
layer, | ||
postcss | ||
) { | ||
const statements = parseStatements(result, styles) | ||
|
||
for (const stmt of statements) { | ||
stmt.media = joinMedia(media, stmt.media || []) | ||
stmt.parentMedia = media | ||
stmt.layer = joinLayer(layer, stmt.layer || []) | ||
|
||
// skip protocol base uri (protocol://url) or protocol-relative | ||
if (stmt.type !== "import" || /^(?:[a-z]+:)?\/\//i.test(stmt.uri)) { | ||
continue | ||
} | ||
|
||
if (options.filter && !options.filter(stmt.uri)) { | ||
// rejected by filter | ||
continue | ||
} | ||
|
||
await resolveImportId(result, stmt, options, state, postcss) | ||
} | ||
|
||
let charset | ||
const imports = [] | ||
const bundle = [] | ||
|
||
function handleCharset(stmt) { | ||
if (!charset) charset = stmt | ||
// charsets aren't case-sensitive, so convert to lower case to compare | ||
else if ( | ||
stmt.node.params.toLowerCase() !== charset.node.params.toLowerCase() | ||
) { | ||
throw new Error( | ||
`Incompatable @charset statements: | ||
${stmt.node.params} specified in ${stmt.node.source.input.file} | ||
${charset.node.params} specified in ${charset.node.source.input.file}` | ||
) | ||
} | ||
} | ||
|
||
// squash statements and their children | ||
statements.forEach(stmt => { | ||
if (stmt.type === "charset") handleCharset(stmt) | ||
else if (stmt.type === "import") { | ||
if (stmt.children) { | ||
stmt.children.forEach((child, index) => { | ||
if (child.type === "import") imports.push(child) | ||
else if (child.type === "charset") handleCharset(child) | ||
else bundle.push(child) | ||
// For better output | ||
if (index === 0) child.parent = stmt | ||
}) | ||
} else imports.push(stmt) | ||
} else if (stmt.type === "media" || stmt.type === "nodes") { | ||
bundle.push(stmt) | ||
} | ||
}) | ||
|
||
return charset ? [charset, ...imports.concat(bundle)] : imports.concat(bundle) | ||
} | ||
|
||
async function resolveImportId(result, stmt, options, state, postcss) { | ||
if (dataURL.isValid(stmt.uri)) { | ||
// eslint-disable-next-line require-atomic-updates | ||
stmt.children = await loadImportContent( | ||
result, | ||
stmt, | ||
stmt.uri, | ||
options, | ||
state, | ||
postcss | ||
) | ||
|
||
return | ||
} | ||
|
||
const atRule = stmt.node | ||
let sourceFile | ||
if (atRule.source?.input?.file) { | ||
sourceFile = atRule.source.input.file | ||
} | ||
const base = sourceFile | ||
? path.dirname(atRule.source.input.file) | ||
: options.root | ||
|
||
const paths = [await options.resolve(stmt.uri, base, options)].flat() | ||
|
||
// Ensure that each path is absolute: | ||
const resolved = await Promise.all( | ||
paths.map(file => { | ||
return !path.isAbsolute(file) ? resolveId(file, base, options) : file | ||
}) | ||
) | ||
|
||
// Add dependency messages: | ||
resolved.forEach(file => { | ||
result.messages.push({ | ||
type: "dependency", | ||
plugin: "postcss-import", | ||
file, | ||
parent: sourceFile, | ||
}) | ||
}) | ||
|
||
const importedContent = await Promise.all( | ||
resolved.map(file => { | ||
return loadImportContent(result, stmt, file, options, state, postcss) | ||
}) | ||
) | ||
|
||
// Merge loaded statements | ||
// eslint-disable-next-line require-atomic-updates | ||
stmt.children = importedContent.flat().filter(x => !!x) | ||
} | ||
|
||
async function loadImportContent( | ||
result, | ||
stmt, | ||
filename, | ||
options, | ||
state, | ||
postcss | ||
) { | ||
const atRule = stmt.node | ||
const { media, layer } = stmt | ||
|
||
assignLayerNames(layer, atRule, state, options) | ||
|
||
if (options.skipDuplicates) { | ||
// skip files already imported at the same scope | ||
if (state.importedFiles[filename]?.[media]?.[layer]) { | ||
return | ||
} | ||
|
||
// save imported files to skip them next time | ||
if (!state.importedFiles[filename]) { | ||
state.importedFiles[filename] = {} | ||
} | ||
if (!state.importedFiles[filename][media]) { | ||
state.importedFiles[filename][media] = {} | ||
} | ||
state.importedFiles[filename][media][layer] = true | ||
} | ||
|
||
const content = await options.load(filename, options) | ||
|
||
if (content.trim() === "") { | ||
result.warn(`${filename} is empty`, { node: atRule }) | ||
return | ||
} | ||
|
||
// skip previous imported files not containing @import rules | ||
if (state.hashFiles[content]?.[media]?.[layer]) { | ||
return | ||
} | ||
|
||
const importedResult = await processContent( | ||
result, | ||
content, | ||
filename, | ||
options, | ||
postcss | ||
) | ||
|
||
const styles = importedResult.root | ||
result.messages = result.messages.concat(importedResult.messages) | ||
|
||
if (options.skipDuplicates) { | ||
const hasImport = styles.some(child => { | ||
return child.type === "atrule" && child.name === "import" | ||
}) | ||
if (!hasImport) { | ||
// save hash files to skip them next time | ||
if (!state.hashFiles[content]) { | ||
state.hashFiles[content] = {} | ||
} | ||
if (!state.hashFiles[content][media]) { | ||
state.hashFiles[content][media] = {} | ||
} | ||
state.hashFiles[content][media][layer] = true | ||
} | ||
} | ||
|
||
// recursion: import @import from imported file | ||
return parseStyles(result, styles, options, state, media, layer, postcss) | ||
} | ||
|
||
module.exports = parseStyles |
Oops, something went wrong.