-
Notifications
You must be signed in to change notification settings - Fork 88
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
10 changed files
with
364 additions
and
34 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,288 @@ | ||
'use strict' | ||
|
||
const sassLoader = require('./webpack.scss-loader.js') | ||
const nodeSass = require("node-sass"); | ||
const path = require('path') | ||
const fs = require('fs-extra') | ||
const co = require('co') | ||
const preview = require('cli-source-preview') | ||
const replaceAsync = require('fast-sass-loader/lib/replace') | ||
const utils = require('fast-sass-loader/lib/utils') | ||
const loaderUtils = require('loader-utils') | ||
const assert = require('assert'); | ||
|
||
|
||
const BOM_HEADER = '\uFEFF' | ||
const EXT_PRECEDENCE = ['.scss', '.sass', '.css'] | ||
const MATCH_URL_ALL = /url\(\s*(['"]?)([^ '"()]+)(\1)\s*\)/g | ||
const MATCH_IMPORTS = /@import\s+(['"])([^,;'"]+)(\1)(\s*,\s*(['"])([^,;'"]+)(\1))*\s*;/g | ||
const MATCH_FILES = /(['"])([^,;'"]+)(\1)/g | ||
|
||
function getImportsToResolve (original, includePaths, transformers) { | ||
let extname = path.extname(original) | ||
let basename = path.basename(original, extname) | ||
let dirname = path.dirname(original) | ||
|
||
let imports = [] | ||
let names = [basename] | ||
let exts = [extname] | ||
let extensionPrecedence = [].concat(EXT_PRECEDENCE, Object.keys(transformers)) | ||
|
||
if (!extname) { | ||
exts = extensionPrecedence | ||
} | ||
if (extname && extensionPrecedence.indexOf(extname) === -1) { | ||
basename = path.basename(original) | ||
names = [basename] | ||
exts = extensionPrecedence | ||
} | ||
if (basename[0] !== '_') { | ||
names.push('_' + basename) | ||
} | ||
|
||
for (let i = 0; i < names.length; i++) { | ||
for (let j = 0; j < exts.length; j++) { | ||
// search relative to original file | ||
imports.push(path.join(dirname, names[i] + exts[j])) | ||
|
||
// search in includePaths | ||
for (let includePath of includePaths) { | ||
imports.push(path.join(includePath, dirname, names[i] + exts[j])) | ||
} | ||
} | ||
} | ||
|
||
return imports | ||
} | ||
|
||
|
||
const cache = [] | ||
|
||
function * mergeSources (opts, entry, resolve, level) { | ||
level = level || 0 | ||
|
||
let includePaths = opts.includePaths | ||
let transformers = opts.transformers | ||
let content = false | ||
|
||
if (typeof entry === 'object') { | ||
content = entry.content | ||
entry = entry.file | ||
} else { | ||
content = yield fs.readFile(entry, 'utf8') | ||
|
||
// fix BOM issue (only on windows) | ||
if (content.startsWith(BOM_HEADER)) { | ||
content = content.substring(BOM_HEADER.length) | ||
} | ||
} | ||
|
||
let ext = path.extname(entry) | ||
|
||
if (transformers[ext]) { | ||
content = transformers[ext](content) | ||
} | ||
|
||
if (opts.data) { | ||
content = opts.data + '\n' + content | ||
} | ||
|
||
let entryDir = path.dirname(entry) | ||
|
||
// replace url(...) | ||
content = content.replace(MATCH_URL_ALL, (total, left, file, right) => { | ||
if (loaderUtils.isUrlRequest(file)) { | ||
// handle url(<loader>!<file>) | ||
let pos = file.lastIndexOf('!') | ||
if (pos >= 0) { | ||
left += file.substring(0, pos + 1) | ||
file = file.substring(pos + 1) | ||
} | ||
|
||
// test again | ||
if (loaderUtils.isUrlRequest(file)) { | ||
let absoluteFile = path.normalize(path.resolve(entryDir, file)) | ||
let relativeFile = path.relative(opts.baseEntryDir, absoluteFile).replace(/\\/g, '/') // fix for windows path | ||
|
||
if (relativeFile[0] !== '.') { | ||
relativeFile = './' + relativeFile | ||
} | ||
|
||
return `url(${left}${relativeFile}${right})` | ||
} else { | ||
return total | ||
} | ||
} else { | ||
return total | ||
} | ||
}) | ||
|
||
// find comments should after content.replace(...), otherwise the comments offset will be incorrect | ||
let commentRanges = utils.findComments(content) | ||
|
||
// replace @import "..." | ||
function * importReplacer (total) { | ||
// if current import is in comments, then skip it | ||
let range = this | ||
let finded = commentRanges.find(commentRange => { | ||
if (range.start >= commentRange[0] && range.end <= commentRange[1]) { | ||
return true | ||
} | ||
}) | ||
|
||
if (finded) { | ||
return total | ||
} | ||
|
||
let contents = [] | ||
let matched | ||
|
||
// must reset lastIndex | ||
MATCH_FILES.lastIndex = 0 | ||
|
||
while (matched = MATCH_FILES.exec(total)) { // eslint-disable-line | ||
let originalImport = matched[2].trim() | ||
if (!originalImport) { | ||
let err = new Error(`import file cannot be empty: "${total}" @${entry}`) | ||
|
||
err.file = entry | ||
|
||
throw err | ||
} | ||
|
||
let imports = getImportsToResolve(originalImport, includePaths, transformers) | ||
let resolvedImport | ||
|
||
for (let i = 0; i < imports.length; i++) { | ||
// if imports[i] is absolute path, then use it directly | ||
if (path.isAbsolute(imports[i]) && fs.existsSync(imports[i])) { | ||
resolvedImport = imports[i] | ||
} else { | ||
try { | ||
let reqFile = loaderUtils.urlToRequest(imports[i], opts.root) | ||
|
||
resolvedImport = yield resolve(entryDir, reqFile) | ||
break | ||
} catch (err) { | ||
// skip | ||
} | ||
} | ||
} | ||
|
||
if (!resolvedImport) { | ||
let err = new Error(`import file cannot be resolved: "${total}" @${entry}`) | ||
|
||
err.file = entry | ||
|
||
throw err | ||
} | ||
|
||
resolvedImport = path.normalize(resolvedImport) | ||
|
||
if (cache.indexOf(resolvedImport) < 0) { | ||
cache.push(resolvedImport) | ||
|
||
contents.push(yield mergeSources(opts, resolvedImport, resolve, level + 1)) | ||
} | ||
} | ||
|
||
return contents.join('\n') | ||
} | ||
|
||
return yield replaceAsync(content, MATCH_IMPORTS, co.wrap(importReplacer)) | ||
} | ||
function resolver (ctx) { | ||
return function (dir, importFile) { | ||
return new Promise((resolve, reject) => { | ||
ctx.resolve(dir, importFile, (err, resolvedFile) => { | ||
if (err) { | ||
reject(err) | ||
} else { | ||
resolve(resolvedFile) | ||
} | ||
}) | ||
}) | ||
} | ||
} | ||
class SassPlugin { | ||
constructor(options) { | ||
this.options = options; | ||
} | ||
|
||
apply(compiler) { | ||
const options = this.options; | ||
// compiler.plugin('compilation', compilation => { | ||
// compilation.plugin('html-webpack-plugin-before-html-generation', | ||
// (htmlPluginData, callback) => { | ||
compiler.plugin('emit', function(compilation, callback) { | ||
const promise = new Promise((resolve, reject) => { | ||
const contents = [] | ||
const browse = function(position) { | ||
if (position < sassLoader.entries.length) { | ||
const entry = sassLoader.entries[position] | ||
|
||
const merged = mergeSources(entry.options, { | ||
file: entry.entry, | ||
content: entry.content | ||
}, resolver(entry.ctx)) | ||
const merged2 = [] | ||
for(const content of merged) { | ||
merged2.push(content) | ||
} | ||
assert.strictEqual(merged2.length, 1) | ||
merged2[0].then((content) => { | ||
console.log("content") | ||
contents.push(content); | ||
position ++; | ||
browse(position); | ||
}, () => { | ||
console.log("111 SCSS dependencies error: ", position, entry.entry) | ||
reject([position, entry.entry]) | ||
}) | ||
} | ||
else { | ||
resolve(contents) | ||
} | ||
} | ||
browse(0); | ||
}); | ||
|
||
promise.then((contents) => { | ||
console.log("contents") | ||
console.log(contents.length) | ||
|
||
|
||
var fs = require('fs'); | ||
fs.writeFile("/tmp/test", contents.join('\n'), function(err) { | ||
if(err) { | ||
return console.log(err); | ||
} | ||
}); | ||
const result = nodeSass.renderSync(Object.assign( | ||
{}, options.sassConfig, { | ||
data: contents.join('\n') | ||
} | ||
)) | ||
const content = result.css; | ||
const srcmap = result.map; | ||
console.log(result) | ||
console.log(srcmap) | ||
console.log(result.includedFiles) | ||
compilation.assets[options.filename] = { | ||
source: () => content, | ||
size: () => content.length | ||
} | ||
compilation.assets[options.filename + '.map'] = { | ||
source: () => srcmap, | ||
size: () => srcmap.length | ||
} | ||
callback(); | ||
}, (position) => { | ||
console.log("222 SCSS dependencies error", position) | ||
callback("SCSS dependencies error" + position); | ||
}); | ||
}); | ||
} | ||
}; | ||
|
||
module.exports = SassPlugin; |
Oops, something went wrong.