Skip to content

Commit

Permalink
change separator char to : (#1)
Browse files Browse the repository at this point in the history
* add error message styling fns

* adds functions apply consistent styling for classnames and modules in error messages

* replace '#' to ':' in import source regex

* update tests to replace separator to ':'

* update docs to reflect new separator

* remove refs to old sep
  • Loading branch information
tusharsnx authored Jun 2, 2022
1 parent aacb62d commit 874e8b8
Show file tree
Hide file tree
Showing 6 changed files with 91 additions and 58 deletions.
14 changes: 7 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -98,13 +98,13 @@ function Component() {

**By default, If plugin found any `'*.module.css'` import, it will transform all our css classes to use style objects.**

If we want to use global css classes, we need to add `'#'` at the end of the class. This will tell plugin not to transform these classes and keep them as is:
If we want to use global css classes, we need to add `':g'` at the end of the class. This will tell plugin not to transform these classes and keep them as is:

```jsx
import "./m1.module.css"

function Component() {
return <h1 className="foo bar# baz"> .... </h1>
return <h1 className="foo bar:g baz"> .... </h1>
}
```

Expand All @@ -117,7 +117,7 @@ function Component() {
}
```

In this example, `'bar'` might be coming from our global stylesheet while `'foo'` and `'baz'` are scoped to the imported module.
In this example, `'bar'` might be declared in the global stylesheet while `'foo'` and `'baz'` are scoped to the imported module.

*The transformed code will use object indexing instead of dot-notation, this helps us to use dashes within our class names (eg. `className="foo-bar baz"`) or else, we would have to use camel-case pattern while using css classes.*

Expand Down Expand Up @@ -163,13 +163,13 @@ eg. we can import two module and use them like:
```jsx
// original

import "./layout.module.css#layout"
import "./component.module.css#comp"
import "./layout.module.css:layout"
import "./component.module.css:comp"

function Component() {
return (
<div className="foo#layout bar#comp baz#layout">
<h1 className="grid-1#layout"> ... </h1>
<div className="foo:layout bar:comp baz:layout">
<h1 className="grid-1:layout"> ... </h1>
</div>
)
}
Expand Down
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ const API = function ({ types: t }: typeof babel): PluginObj<PluginPass> {
// stores previous css-modules found on the same file
let modules: Modules = state.pluginState.modules ?? { namedModules: {} }

if (!t.isImportDeclaration(path.node) || !/.module.(s[ac]ss|css)(#.*)?$/iu.test(path.node.source.value)) return
if (!t.isImportDeclaration(path.node) || !/.module.(s[ac]ss|css)(:.*)?$/iu.test(path.node.source.value)) return

// saving path for error messages
CSSModuleError.path = path
Expand Down
2 changes: 1 addition & 1 deletion src/transforms.ts
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ export const getImportInfo = (statement: t.ImportDeclaration): DefaultModule | M
}
}

// eg. import "./moduleA.module.css#m1"
// eg. import "./moduleA.module.css:m1"
return {
moduleSource: module.moduleSource,
moduleName: module.moduleName,
Expand Down
33 changes: 23 additions & 10 deletions src/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,56 +2,69 @@ import { NodePath } from "@babel/core"
import chalk from "chalk"

/**
* splits full classname (with '#') into classname and module name
* splits full classname (with ':') into classname and module name
*
* @param classname full classname with module
* @param defaultModule default module for the file
* @returns classname and module name used
*/
export const splitClsName = (classname: string, defaultModule: string): { classname: string; module?: string } => {
if (shouldTransform(classname)) {
// TODO: throw error if more than one '#' is present
let [splittedClassName, module] = classname.split("#")
// TODO: throw error if more than one sep is present, or use last sep in the classname to split
let [splittedClassName, module] = classname.split(":")
if (module === "") {
throw new CSSModuleError(`no module name found after ':' on ${CSSModuleError.cls(classname)}`)
}
return {
classname: splittedClassName.trim(),
module: module || defaultModule,
}
} else {
// global class
return {
// global class
classname: classname.slice(0, classname.length - 1),
classname: classname.slice(0, classname.length - 2),
}
}
}

export const shouldTransform = (classname: string) => {
return !classname.endsWith("#")
return !classname.endsWith(":g")
}

export const splitClassnames = (classes: string) => {
return classes.split(" ")
}

export const splitModuleSource = (source: string): { moduleSource: string; moduleName?: string } => {
if (!source.includes("#")) {
if (!source.includes(":")) {
return {
moduleSource: source,
}
}
let [moduleSource, moduleName] = source.split("#")
let [moduleSource, moduleName] = source.split(":")
return { moduleSource, moduleName }
}

export class CSSModuleError extends Error {
errorMessage: string
static path: NodePath
static path: NodePath | undefined

constructor(errorMessage: string) {
super()
this.errorMessage = errorMessage
this.name = chalk.red("CSSModuleError")
this.message = `at (${CSSModuleError.path.node.loc?.start.line}:${CSSModuleError.path.node.loc?.start.column})
this.message = `at (${CSSModuleError.path?.node.loc?.start.line}:${
CSSModuleError.path?.node.loc?.start.column
}):
${this.errorMessage.replace(/ +/g, " ")}
`.replace(/ +/g, " ")
}

static cls(cls: string) {
return `'${chalk.cyan(cls)}'`
}

static mod(mod: string) {
return `'${chalk.cyan(mod)}'`
}
}
73 changes: 42 additions & 31 deletions tests/transforms.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,21 +31,21 @@ describe("single imports", () => {
})

test("with specifier (ignore names)", async () => {
let source = `import style from "./foo.module.scss#m1"`
let source = `import style from "./foo.module.scss:m1"`
let code = await runWithBabel(source)
expect(code).toMatchInlineSnapshot(`"import style from \\"./foo.module.scss\\";"`)

source = `import style from "./foo.module.css#m1"`
source = `import style from "./foo.module.css:m1"`
code = await runWithBabel(source)
expect(code).toMatchInlineSnapshot(`"import style from \\"./foo.module.css\\";"`)
})

test("with named-module", async () => {
let source = `import "./foo.module.scss#m1"`
let source = `import "./foo.module.scss:m1"`
let code = await runWithBabel(source)
expect(code).toMatchInlineSnapshot(`"import _m from \\"./foo.module.scss\\";"`)

source = `import "./foo.module.css#m1"`
source = `import "./foo.module.css:m1"`
code = await runWithBabel(source)
expect(code).toMatchInlineSnapshot(`"import _m from \\"./foo.module.css\\";"`)
})
Expand All @@ -72,8 +72,8 @@ describe("imports multiple module", () => {

test("with named-modules", async () => {
let source = `
import "./module1.module.css#m1"
import "./module2.module.css#m2"
import "./module1.module.css:m1"
import "./module2.module.css:m2"
`
let code = await runWithBabel(source)
expect(code).toMatchInlineSnapshot(`
Expand All @@ -84,16 +84,16 @@ import _m2 from \\"./module2.module.css\\";"

test("with same named-modules twice", async () => {
let source = `
import "./module1.module.css#m1"
import "./module2.module.css#m1"
import "./module1.module.css:m1"
import "./module2.module.css:m1"
`
await expect(runWithBabel(source)).rejects.toThrow(CSSModuleError)
})

test("with specifier", async () => {
let source = `
import style from "./module1.module.css#m1"
import style1 from "./module2.module.css#m2"
import style from "./module1.module.css:m1"
import style1 from "./module2.module.css:m2"
`
let code = await runWithBabel(source)
expect(code).toMatchInlineSnapshot(`
Expand All @@ -104,8 +104,8 @@ import style1 from \\"./module2.module.css\\";"

test("with same specifier twice", async () => {
let source = `
import style from "./module1.module.css#m1"
import style from "./module2.module.css#m2"
import style from "./module1.module.css:m1"
import style from "./module2.module.css:m2"
`
expect(runWithBabel(source)).rejects.toThrow(SyntaxError)
})
Expand All @@ -115,7 +115,7 @@ describe("different kinds together", () => {
test("each kind once", async () => {
let source = `
import style from "./module1.module.css"
import "./module2.module.css#m2"
import "./module2.module.css:m2"
import "./module3.module.css"
`
let code = await runWithBabel(source)
Expand All @@ -130,15 +130,15 @@ import _style from \\"./module3.module.css\\";"
let source = `
import "./component.module.css"
import layout from "./layout.module.css"
import "./layout2.module.css#layout"
import "./layout2.module.css:layout"
`
await expect(runWithBabel(source)).rejects.toThrow(CSSModuleError)
})

test("same module name used twice on different kinds (2)", async () => {
let source = `
import "./component.module.css"
import "./layout2.module.css#layout"
import "./layout2.module.css:layout"
import layout from "./layout.module.css"
`
await expect(runWithBabel(source)).rejects.toThrow(CSSModuleError)
Expand Down Expand Up @@ -184,10 +184,10 @@ function Component() {

test("with named-module", async () => {
let source = `
import "./component.module.css#m1"
import "./component.module.css:m1"
function Component() {
return <h1 className="foo-bar#m1 baz#m1"></h1>
return <h1 className="foo-bar:m1 baz:m1"></h1>
}
`
let code = await runWithBabel(source)
Expand All @@ -199,6 +199,17 @@ function Component() {
}"
`)
})

test("named module class with colon only", async () => {
let source = `
import "./component.module.css"
function Component() {
return <h1 className="foo-bar: baz"></h1>
}
`
await expect(() => runWithBabel(source)).rejects.toThrow(CSSModuleError)
})
})

describe("jsx with multiple modules", () => {
Expand Down Expand Up @@ -228,7 +239,7 @@ function component() {
import layout from "./layout.module.css"
function Component() {
return <h1 className="foo-bar#layout baz#style"></h1>
return <h1 className="foo-bar:layout baz:style"></h1>
}
`
let code = await runWithBabel(source)
Expand All @@ -244,11 +255,11 @@ function Component() {

test("named-module", async () => {
let source = `
import "./component.module.css#style"
import "./layout.module.css#layout"
import "./component.module.css:style"
import "./layout.module.css:layout"
function Component() {
return <h1 className="foo-bar#layout baz#style"></h1>
return <h1 className="foo-bar:layout baz:style"></h1>
}
`
let code = await runWithBabel(source)
Expand All @@ -268,19 +279,19 @@ function Component() {
import layout from "./layout.module.css"
function Component() {
return <h1 className="foo-bar#layout baz#style2"></h1>
return <h1 className="foo-bar:layout baz:style2"></h1>
}
`
await expect(runWithBabel(source)).rejects.toThrow(CSSModuleError)
})

test("named-module (class uses non-existent module)", async () => {
let source = `
import "./component.module.css#style"
import "./layout.module.css#layout"
import "./component.module.css:style"
import "./layout.module.css:layout"
function component() {
return <h1 className="foo-bar#layout baz#style2"></h1>
return <h1 className="foo-bar:layout baz:style2"></h1>
}
`
await expect(runWithBabel(source)).rejects.toThrow(CSSModuleError)
Expand All @@ -292,12 +303,12 @@ describe("jsx with multiple kinds of module", () => {
let source = `
import style from "./component.module.css"
import layout from "./layout.module.css"
import "./layout2.module.css#altLayout"
import "./layout2.module.css:altLayout"
function Component() {
return (
<div className="grid-1#layout col-3#altLayout">
<h1 className="clr-green#style"></h1>
<div className="grid-1:layout col-3:altLayout">
<h1 className="clr-green:style"></h1>
</div>
)
}
Expand All @@ -320,12 +331,12 @@ function Component() {
let source = `
import style from "./component.module.css"
import layout from "./layout.module.css"
import "./layout2.module.css#altLayout"
import "./layout2.module.css:altLayout"
function Component() {
return (
<div className="grid-1#layout col-3#altLayout">
<h1 className="bg-primary# clr-green#style"></h1>
<div className="grid-1:layout col-3:altLayout">
<h1 className="bg-primary:g clr-green:style"></h1>
</div>
)
}
Expand Down
Loading

0 comments on commit 874e8b8

Please sign in to comment.