Skip to content

Commit

Permalink
feat: adds stylus & less preprocessor (#5)
Browse files Browse the repository at this point in the history
* test: style preprocess

* fix: postcss is undefined issue by using import syntax

* feat: adds less preprocessor

* feat: adds stylus preprocessor

* chore: remove unnecessary dir in test script

* chore: add circleci config
  • Loading branch information
znck authored Apr 6, 2018
1 parent 9204f16 commit f2fd8b9
Show file tree
Hide file tree
Showing 9 changed files with 671 additions and 54 deletions.
31 changes: 31 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
# Javascript Node CircleCI 2.0 configuration file
#
# Check https://circleci.com/docs/2.0/language-javascript/ for more details
#
version: 2
jobs:
build:
docker:
- image: circleci/node:6

working_directory: ~/repo

steps:
- checkout

# Download and cache dependencies
- restore_cache:
keys:
- v1-dependencies-{{ checksum "package.json" }}
# fallback to using the latest cache if no exact match is found
- v1-dependencies-

- run: yarn install

- save_cache:
paths:
- node_modules
key: v1-dependencies-{{ checksum "package.json" }}

# run tests!
- run: yarn test
3 changes: 2 additions & 1 deletion jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,5 +3,6 @@ module.exports = {
transform: {
'^.+\\.jsx?$': 'babel-jest',
'^.+\\.tsx?$': '<rootDir>/node_modules/ts-jest/preprocessor.js'
}
},
testMatch: ['**/?(*.)(spec|test).ts']
}
6 changes: 4 additions & 2 deletions lib/compileStyle.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { ProcessOptions, LazyResult } from 'postcss'
import postcss = require('postcss')
import postcss, { ProcessOptions, LazyResult } from 'postcss'
import trimPlugin from './stylePlugins/trim'
import scopedPlugin from './stylePlugins/scoped'
import { processors, StylePreprocessor, StylePreprocessorResults } from './styleProcessors'
Expand Down Expand Up @@ -59,6 +58,9 @@ export function compileStyle (

let result, code, outMap
const errors = []
if (preProcessedSource && preProcessedSource.errors.length) {
errors.push(...preProcessedSource.errors)
}
try {
result = postcss(plugins).process(source, postCSSOptions)
// force synchronous transform (we know we only have sync plugins)
Expand Down
2 changes: 1 addition & 1 deletion lib/stylePlugins/scoped.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Root } from 'postcss'
import postcss = require('postcss')
import * as postcss from 'postcss'
// postcss-selector-parser does have typings but it's problematic to work with.
const selectorParser = require('postcss-selector-parser')

Expand Down
2 changes: 1 addition & 1 deletion lib/stylePlugins/trim.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { Root } from 'postcss'
import postcss = require('postcss')
import * as postcss from 'postcss'

export default postcss.plugin('trim', () => (css: Root) => {
css.walk(({ type, raws }) => {
Expand Down
88 changes: 80 additions & 8 deletions lib/styleProcessors/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ export interface StylePreprocessor {
export interface StylePreprocessorResults {
code: string
map?: any
errors: Array<Error>
}

// .scss/.sass processor
Expand All @@ -24,19 +25,25 @@ const scss: StylePreprocessor = {
const finalOptions = Object.assign({}, options, {
data: source,
file: options.filename,
outFile: options.filename,
sourceMap: !!map
})

const result = nodeSass.renderSync(finalOptions)
try {
const result = nodeSass.renderSync(finalOptions)

if (map) {
return {
code: result.css.toString(),
map: merge(map, JSON.parse(result.map.toString()))
if (map) {
return {
code: result.css.toString(),
map: merge(map, JSON.parse(result.map.toString())),
errors: []
}
}
}

return { code: result.css.toString() }
return { code: result.css.toString(), errors: [] }
} catch (e) {
return { code: '', errors: [e] }
}
}
}

Expand All @@ -54,7 +61,72 @@ const sass = {
}
}

// .less
const less = {
render(
source: string,
map: any | null,
options: any
): StylePreprocessorResults {
const nodeLess = require('less')

let result: any
let error: Error | null = null
nodeLess.render(
source,
{ syncImport: true },
(err: Error | null, output: any) => {
error = err
result = output
}
)

if (error) return { code: '', errors: [error] }

if (map) {
return {
code: result.css.toString(),
map: merge(map, result.map),
errors: []
}
}

return { code: result.css.toString(), errors: [] }
}
}

// .styl
const styl = {
render(
source: string,
map: any | null,
options: any
): StylePreprocessorResults {
const nodeStylus = require('stylus')
try {
const ref = nodeStylus(source)
Object.keys(options).forEach(key => ref.set(key, options[key]))
if (map) ref.set('sourcemap', { inline: false, comment: false })

const result = ref.render()
if (map) {
return {
code: result,
map: merge(map, ref.sourcemap),
errors: []
}
}

return { code: result, errors: [] }
} catch (e) {
return { code: '', errors: [e] }
}
}
}

export const processors: { [key: string]: StylePreprocessor } = {
less,
sass,
scss,
sass
styl
}
3 changes: 3 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@
"devDependencies": {
"@types/node": "^9.4.7",
"jest": "^22.4.2",
"less": "^3.0.1",
"node-sass": "^4.8.3",
"pug": "^2.0.3",
"stylus": "^0.54.5",
"ts-jest": "^22.4.2",
"typescript": "^2.7.2",
"vue-template-compiler": "^2.5.16"
Expand Down
100 changes: 100 additions & 0 deletions test/compileStyle.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { parse } from '../lib/parse'
import { compileStyle } from '../lib/compileStyle'

test('preprocess less', () => {
const style = parse({
source:
'<style lang="less">\n' +
'@red: rgb(255, 0, 0);\n' +
'.color { color: @red; }\n' +
'</style>\n',
filename: 'example.vue',
needMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})

expect(result.errors.length).toBe(0)
expect(result.code).toEqual(expect.stringContaining('color: #ff0000;'))
expect(result.map).toBeTruthy()
})

test('preprocess scss', () => {
const style = parse({
source:
'<style lang="scss">\n' +
'$red: rgb(255, 0, 0);\n' +
'.color { color: $red; }\n' +
'</style>\n',
filename: 'example.vue',
needMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})

expect(result.errors.length).toBe(0)
expect(result.code).toEqual(expect.stringContaining('color: red;'))
expect(result.map).toBeTruthy()
})

test('preprocess sass', () => {
const style = parse({
source:
'<style lang="sass">\n' +
'$red: rgb(255, 0, 0);\n' +
'.color\n' +
' color: $red\n' +
'</style>\n',
filename: 'example.vue',
needMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})

expect(result.errors.length).toBe(0)
expect(result.code).toEqual(expect.stringContaining('color: red;'))
expect(result.map).toBeTruthy()
})

test('preprocess stylus', () => {
const style = parse({
source:
'<style lang="styl">\n' +
'red-color = rgb(255, 0, 0);\n' +
'.color\n' +
' color: red-color\n' +
'</style>\n',
filename: 'example.vue',
needMap: true
}).styles[0]
const result = compileStyle({
id: 'v-scope-xxx',
filename: 'example.vue',
source: style.content,
map: style.map,
scoped: false,
preprocessLang: style.lang
})

expect(result.errors.length).toBe(0)
expect(result.code).toEqual(expect.stringContaining('color: #f00;'))
expect(result.map).toBeTruthy()
})
Loading

0 comments on commit f2fd8b9

Please sign in to comment.