Skip to content

Commit

Permalink
support scoped id
Browse files Browse the repository at this point in the history
  • Loading branch information
egoist committed Apr 1, 2018
1 parent d02ce59 commit fc988db
Show file tree
Hide file tree
Showing 9 changed files with 334 additions and 89 deletions.
5 changes: 3 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -36,19 +36,20 @@
"jest": "^22.0.4",
"less": "^2.7.3",
"node-sass": "^4.7.2",
"rollup": "^0.53.3",
"rollup": "^0.57.1",
"stylus": "^0.54.5",
"sugarss": "^1.0.1",
"xo": "^0.18.2"
},
"dependencies": {
"@vue/component-compiler-utils": "^1.0.0",
"chalk": "^2.0.0",
"concat-with-sourcemaps": "^1.0.5",
"cssnano": "^3.10.0",
"fs-extra": "^5.0.0",
"import-cwd": "^2.1.0",
"pify": "^3.0.0",
"postcss": "^6.0.1",
"postcss": "^6.0.21",
"postcss-load-config": "^1.2.0",
"postcss-modules": "^1.1.0",
"promise.series": "^0.2.0",
Expand Down
47 changes: 30 additions & 17 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ function inferOption(option, defaultValue) {
return option ? {} : defaultValue
}

const SCOPED_REGEXP = /\?scoped=(.+)$/

export default (options = {}) => {
const filter = createFilter(options.include, options.exclude)
const sourceMap = options.sourceMap
Expand Down Expand Up @@ -44,16 +46,6 @@ export default (options = {}) => {
}
let use = options.use || ['sass', 'stylus', 'less']
use.unshift(['postcss', postcssLoaderOptions])
use = use.reduce((res, rule) => {
if (typeof rule === 'string') {
rule = [rule]
}
const name = rule[0]
const options = rule[1] || {}

res[name] = options
return res
}, {})

const loaders = new Loaders({
use,
Expand All @@ -65,7 +57,25 @@ export default (options = {}) => {
return {
name: 'postcss',

resolveId(id, importer) {
if (importer && SCOPED_REGEXP.test(id)) {
return path.resolve(path.dirname(importer), id)
}
},

load(id) {
if (SCOPED_REGEXP.test(id)) {
return fs.readFile(id.replace(SCOPED_REGEXP, ''), 'utf8')
}
},

async transform(code, id) {
let scoped
if (SCOPED_REGEXP.test(id)) {
scoped = SCOPED_REGEXP.exec(id)[1]
id = id.replace(SCOPED_REGEXP, '')
}

if (!filter(id) || !loaders.isSupported(id)) {
return null
}
Expand All @@ -78,7 +88,8 @@ export default (options = {}) => {
code,
map: undefined,
id,
sourceMap
sourceMap,
scoped
})

if (postcssLoaderOptions.extract) {
Expand Down Expand Up @@ -147,12 +158,14 @@ export default (options = {}) => {
}

const { code, codeFilePath, map, mapFilePath } = getExtracted()
await fs.ensureDir(path.dirname(codeFilePath))
.then(() => Promise.all([
fs.writeFile(codeFilePath, code, 'utf8'),
sourceMap === true &&
fs.writeFile(mapFilePath, map, 'utf8')
]))
await fs
.ensureDir(path.dirname(codeFilePath))
.then(() =>
Promise.all([
fs.writeFile(codeFilePath, code, 'utf8'),
sourceMap === true && fs.writeFile(mapFilePath, map, 'utf8')
])
)
}
}
}
27 changes: 16 additions & 11 deletions src/loaders.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,15 @@ import lessLoader from './less-loader'

export default class Loaders {
constructor(options = {}) {
this.use = options.use
this.use = options.use.map(rule => {
if (typeof rule === 'string') {
return [rule]
}
if (Array.isArray(rule)) {
return rule
}
throw new TypeError('The rule in `use` option must be string or Array!')
})
this.loaders = []

this.registerLoader(postcssLoader)
Expand Down Expand Up @@ -34,24 +42,21 @@ export default class Loaders {

isSupported(filepath) {
return this.loaders.some(loader => {
return loader.test.test(filepath)
return loader.test && loader.test.test(filepath)
})
}

process({ code, map, id, sourceMap }) {
const names = Object.keys(this.use)
return series(names.slice().reverse().map(name => {
process({ code, map, id, sourceMap, scoped }) {
return series(this.use.slice().reverse().map(([name, options]) => {
const loader = this.getLoader(name)
const options = this.use[name]
const loaderContext = {
options,
options: options || {},
id,
sourceMap
sourceMap,
scoped
}
return v => {
// Only process if it's postcss loader
// Or passed `test`
if (name === 'postcss' || loader.test.test(id)) {
if (loader.alwaysProcess || loader.test.test(id)) {
return loader.process.call(loaderContext, v)
}
// Otherwise directly return input value
Expand Down
56 changes: 35 additions & 21 deletions src/postcss-loader.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,13 @@ import importCwd from 'import-cwd'
import postcss from 'postcss'
import findPostcssConfig from 'postcss-load-config'
import reserved from 'reserved-words'
import scopedPlugin from '@vue/component-compiler-utils/dist/stylePlugins/scoped'
import humanlizePath from './utils/humanlize-path'
import normalizePath from './utils/normalize-path'

const styleInjectPath = require.resolve('style-inject/dist/style-inject.es').replace(/[\\/]+/g, '/')
const styleInjectPath = require
.resolve('style-inject/dist/style-inject.es')
.replace(/[\\/]+/g, '/')

function loadConfig(id, { ctx: configOptions, path: configPath }) {
const handleError = err => {
Expand Down Expand Up @@ -54,14 +57,18 @@ function isModuleFile(file) {

export default {
name: 'postcss',
alwaysProcess: true,
test: /\.(css|sss)$/,
async process({ code, map }) {
const config = this.options.config ?
await loadConfig(this.id, this.options.config) :
{}
const config = this.options.config
? await loadConfig(this.id, this.options.config)
: {}

const options = this.options
const plugins = [...(options.postcss.plugins || []), ...(config.plugins || [])]
const plugins = [
...(options.postcss.plugins || []),
...(config.plugins || [])
]
const shouldExtract = options.extract
const shouldInject = options.inject

Expand All @@ -73,7 +80,9 @@ export default {
require('postcss-modules')({
// In tests
// Skip hash in names since css content on windows and linux would differ because of `new line` (\r?\n)
generateScopedName: process.env.ROLLUP_POSTCSS_TEST ? '[name]_[local]' : '[name]_[local]__[hash:base64:5]',
generateScopedName: process.env.ROLLUP_POSTCSS_TEST
? '[name]_[local]'
: '[name]_[local]__[hash:base64:5]',
...options.modules,
getJSON(filepath, json) {
modulesExported[filepath] = json
Expand All @@ -86,17 +95,21 @@ export default {
plugins.push(require('cssnano')(options.minimize))
}

if (this.scoped) {
plugins.unshift(scopedPlugin(this.scoped))
}

const postcssOpts = {
...this.options.postcss,
...config.options,
// Followings are never modified by user config config
from: this.id,
to: this.id,
map: this.sourceMap ?
shouldExtract ?
{ inline: false, annotation: false } :
{ inline: true, annotation: false } :
false
map: this.sourceMap
? shouldExtract
? { inline: false, annotation: false }
: { inline: true, annotation: false }
: false
}
delete postcssOpts.plugins

Expand All @@ -119,24 +132,25 @@ export default {

if (options.namedExports) {
const json = modulesExported[this.id]
const getClassName = typeof options.namedExports === 'function' ?
options.namedExports :
ensureClassName
const getClassName =
typeof options.namedExports === 'function'
? options.namedExports
: ensureClassName
// eslint-disable-next-line guard-for-in
for (const name in json) {
const newName = getClassName(name)
// Log transformed class names
// But skip this when namedExports is a function
// Since a user like you can manually log that if you want
if (name !== newName && typeof options.namedExports !== 'function') {
console.warn(`Exported "${name}" as "${newName}" in ${humanlizePath(this.id)}`)
console.warn(
`Exported "${name}" as "${newName}" in ${humanlizePath(this.id)}`
)
}
if (!json[newName]) {
json[newName] = json[name]
}
output += `export var ${newName} = ${JSON.stringify(
json[name]
)};\n`
output += `export var ${newName} = ${JSON.stringify(json[name])};\n`
}
}

Expand All @@ -154,9 +168,9 @@ export default {
}
if (!shouldExtract && shouldInject) {
output += `\nimport styleInject from '${styleInjectPath}';\nstyleInject(css${
Object.keys(options.inject).length > 0 ?
`,${JSON.stringify(options.inject)}` :
''
Object.keys(options.inject).length > 0
? `,${JSON.stringify(options.inject)}`
: ''
});`
}

Expand Down
Loading

0 comments on commit fc988db

Please sign in to comment.