Skip to content

Commit

Permalink
wip rewrite
Browse files Browse the repository at this point in the history
  • Loading branch information
romainmenke committed Jul 18, 2023
1 parent 6b5424e commit 647184e
Show file tree
Hide file tree
Showing 17 changed files with 473 additions and 324 deletions.
6 changes: 3 additions & 3 deletions index.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
const path = require("path")

// internal tooling
const applyMedia = require("./lib/apply-media")
const applyConditions = require("./lib/apply-conditions")
const applyRaws = require("./lib/apply-raws")
const applyStyles = require("./lib/apply-styles")
const loadContent = require("./lib/load-content")
Expand Down Expand Up @@ -61,12 +61,12 @@ function AtImport(options) {
options,
state,
[],
[],
"",
postcss
)

applyRaws(bundle)
applyMedia(bundle, options, state, atRule)
applyConditions(bundle, options, state, atRule)
applyStyles(bundle, styles)
},
}
Expand Down
129 changes: 129 additions & 0 deletions lib/apply-conditions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,129 @@
"use strict"

const assignLayerNames = require("./assign-layer-names")
const joinLayer = require("./join-layer")
const joinMedia = require("./join-media")
const joinSupports = require("./join-supports")

module.exports = function (bundle, options, state, atRule) {
bundle.forEach(stmt => {
if (!stmt.conditions.length || stmt.type === "charset") {
return
}

if (1 < stmt.conditions.filter(x => x.layer.length > 0).length) {
for (const condition of stmt.conditions) {
if (condition.layer.length > 0) {
assignLayerNames(condition.layer, stmt.node, state, options)
}
}
}

if (stmt.type === "import") {
const parts = [stmt.fullUri]

let media = []
let supports = []
let layer = []

for (const condition of stmt.conditions) {
media = joinMedia(media, condition.media)
supports = joinSupports(supports, condition.supports)
layer = joinLayer(layer, condition.layer)
}

if (layer.length) {
const layerName = layer.join(".")

let layerParams = "layer"
if (layerName) {
layerParams = `layer(${layerName})`
}

parts.push(layerParams)
}

if (supports.length) {
if (supports.length === 1) {
parts.push(`supports(${supports[0]})`)
} else {
parts.push(`supports(${supports.map(x => `(${x})`).join(" and ")})`)
}
}

if (media.length) {
parts.push(media.join(", "))
}

stmt.node.params = parts.join(" ")

return
}

const { nodes } = stmt
const { parent } = nodes[0]

const atRules = []

// Convert conditions to at-rules
for (const condition of stmt.conditions) {
if (condition.media.length > 0) {
const mediaNode = atRule({
name: "media",
params: condition.media.join(", "),
source: parent.source,
})

atRules.push(mediaNode)
}

if (condition.supports.length > 0) {
const supportsNode = atRule({
name: "supports",
params:
condition.supports.length === 1
? `(${condition.supports[0]})`
: condition.supports.map(x => `(${x})`).join(" and "),
source: parent.source,
})

atRules.push(supportsNode)
}

if (condition.layer.length > 0) {
const layerNode = atRule({
name: "layer",
params: condition.layer.join("."),
source: parent.source,
})

atRules.push(layerNode)
}
}

// Add nodes to AST
for (let i = 0; i < atRules.length - 1; i++) {
atRules[i].append(atRules[i + 1])
}

const innerAtRule = atRules[atRules.length - 1]
const outerAtRule = atRules[0]

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 = "nodes"
stmt.nodes = [outerAtRule]
delete stmt.node
})
}
114 changes: 0 additions & 114 deletions lib/apply-media.js

This file was deleted.

2 changes: 1 addition & 1 deletion lib/apply-styles.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ module.exports = function (bundle, styles) {

// Strip additional statements.
bundle.forEach(stmt => {
if (["charset", "import", "media"].includes(stmt.type)) {
if (["charset", "import"].includes(stmt.type)) {
stmt.node.parent = undefined
styles.append(stmt.node)
} else if (stmt.type === "nodes") {
Expand Down
88 changes: 88 additions & 0 deletions lib/check-for-cycles.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
"use strict"

// This is a modified version of toposort.
// Instead of throwing on cycles, it returns true.
function checkForCycles(edges) {
const nodes = uniqueNodes(edges)

let cursor = nodes.length
const sorted = new Array(cursor)
const visited = {}
let i = cursor
const outgoingEdges = makeOutgoingEdges(edges)
const nodesHash = makeNodesHash(nodes)

while (i--) {
if (visited[i]) {
continue
}

if (visit(nodes[i], i, new Set())) {
return true
}
}

return false

function visit(node, i, predecessors) {
if (predecessors.has(node)) {
// Has a cycle.
return true
}

if (visited[i]) {
return false
}

visited[i] = true

let outgoing = outgoingEdges.get(node) || new Set()
outgoing = Array.from(outgoing)

if ((i = outgoing.length)) {
predecessors.add(node)
do {
const child = outgoing[--i]
if (visit(child, nodesHash.get(child), predecessors)) {
return true
}
} while (i)
predecessors.delete(node)
}

sorted[--cursor] = node

return false
}
}

function uniqueNodes(arr) {
const res = new Set()
for (let i = 0, len = arr.length; i < len; i++) {
const edge = arr[i]
res.add(edge[0])
res.add(edge[1])
}
return Array.from(res)
}

function makeOutgoingEdges(arr) {
const edges = new Map()
for (let i = 0, len = arr.length; i < len; i++) {
const edge = arr[i]
if (!edges.has(edge[0])) edges.set(edge[0], new Set())
if (!edges.has(edge[1])) edges.set(edge[1], new Set())
edges.get(edge[0]).add(edge[1])
}
return edges
}

function makeNodesHash(arr) {
const res = new Map()
for (let i = 0, len = arr.length; i < len; i++) {
res.set(arr[i], i)
}
return res
}

module.exports = checkForCycles
20 changes: 16 additions & 4 deletions lib/data-url.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,26 @@
"use strict"

const dataURLRegexp = /^data:text\/css;base64,/i
const anyDataURLRegexp = /^data:text\/css(?:;(base64|plain))?,/i
const base64DataURLRegexp = /^data:text\/css;base64,/i
const plainDataURLRegexp = /^data:text\/css;plain,/i

function isValid(url) {
return dataURLRegexp.test(url)
return anyDataURLRegexp.test(url)
}

function contents(url) {
// "data:text/css;base64,".length === 21
return Buffer.from(url.slice(21), "base64").toString()
if (base64DataURLRegexp.test(url)) {
// "data:text/css;base64,".length === 21
return Buffer.from(url.slice(21), "base64").toString()
}

if (plainDataURLRegexp.test(url)) {
// "data:text/css;plain,".length === 20
return decodeURIComponent(url.slice(20))
}

// "data:text/css,".length === 14
return decodeURIComponent(url.slice(14))
}

module.exports = {
Expand Down

0 comments on commit 647184e

Please sign in to comment.