Skip to content

Commit

Permalink
Fix @charset handling
Browse files Browse the repository at this point in the history
Fixes #436
Closes #438
  • Loading branch information
RyanZim committed Dec 12, 2020
1 parent 443c112 commit f7157cf
Show file tree
Hide file tree
Showing 7 changed files with 74 additions and 6 deletions.
29 changes: 23 additions & 6 deletions index.js
Expand Up @@ -107,10 +107,7 @@ function AtImport(options) {

// Strip additional statements.
bundle.forEach(stmt => {
if (stmt.type === "import") {
stmt.node.parent = undefined
styles.append(stmt.node)
} else if (stmt.type === "media") {
if (["charset", "import", "media"].includes(stmt.type)) {
stmt.node.parent = undefined
styles.append(stmt.node)
} else if (stmt.type === "nodes") {
Expand Down Expand Up @@ -150,15 +147,33 @@ function AtImport(options) {
}, Promise.resolve())
})
.then(() => {
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 === "import") {
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
Expand All @@ -169,7 +184,9 @@ function AtImport(options) {
}
})

return imports.concat(bundle)
return charset
? [charset, ...imports.concat(bundle)]
: imports.concat(bundle)
})
}

Expand Down
9 changes: 9 additions & 0 deletions lib/parse-statements.js
Expand Up @@ -29,6 +29,7 @@ module.exports = function (result, styles) {
if (node.type === "atrule") {
if (node.name === "import") stmt = parseImport(result, node)
else if (node.name === "media") stmt = parseMedia(result, node)
else if (node.name === "charset") stmt = parseCharset(result, node)
}

if (stmt) {
Expand Down Expand Up @@ -64,6 +65,14 @@ function parseMedia(result, atRule) {
}
}

function parseCharset(result, atRule) {
return {
type: "charset",
node: atRule,
media: [],
}
}

function parseImport(result, atRule) {
let prev = getPrev(atRule)
if (prev) {
Expand Down
2 changes: 2 additions & 0 deletions test/fixtures/charset-error.css
@@ -0,0 +1,2 @@
@charset "foobar";
@import "imports/charset.css";
4 changes: 4 additions & 0 deletions test/fixtures/charset-import.css
@@ -0,0 +1,4 @@
@charset "UTF-8";
@import "test/fixtures/imports/foo.css";
@import "test/fixtures/imports/charset.css";
bar{}
3 changes: 3 additions & 0 deletions test/fixtures/charset-import.expected.css
@@ -0,0 +1,3 @@
@charset "UTF-8";
foo{}
bar{}
1 change: 1 addition & 0 deletions test/fixtures/imports/charset.css
@@ -0,0 +1 @@
@charset "UTF-8";
32 changes: 32 additions & 0 deletions test/import.js
Expand Up @@ -46,6 +46,38 @@ test("should not fail with absolute and local import", t => {
.then(result => t.is(result.css, "@import url('http://');\nfoo{}"))
})

test("should keep @charset first", t => {
const base = '@charset "UTF-8";\n@import url(http://);'
return postcss()
.use(atImport())
.process(base, { from: undefined })
.then(result => {
t.is(result.warnings().length, 0)
t.is(result.css, base)
})
})

test(
"should handle multiple @charset statements",
checkFixture,
"charset-import"
)

test("should error if incompatable @charset statements", t => {
t.plan(2)
const file = "test/fixtures/charset-error.css"
return postcss()
.use(atImport())
.process(readFileSync(file), { from: file })
.catch(err => {
t.truthy(err)
t.regex(
err.message,
/Incompatable @charset statements:.+specified in.+specified in.+/s
)
})
})

test("should error when file not found", t => {
t.plan(1)
const file = "test/fixtures/imports/import-missing.css"
Expand Down

0 comments on commit f7157cf

Please sign in to comment.