-
Notifications
You must be signed in to change notification settings - Fork 414
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Webpack loader #186
Webpack loader #186
Changes from 2 commits
1dfbf8f
44bb724
d104ae5
5b632bd
7adcf67
b9ca27a
4632b2d
ca187d9
0ce4c4c
aa3d2a9
e124b4b
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
module.exports = require('./build/tools/webpack-loader').default; | ||
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,130 @@ | ||
/* @flow */ | ||
|
||
import * as babel from 'babel-core'; | ||
|
||
function shouldRunLinaria(source: string) { | ||
return ( | ||
(/import .+ from ['"]linaria['"]/g.test(source) || | ||
/require\(['"]linaria['"]\)/g.test(source)) && | ||
/css(\.named)?`/g.test(source) | ||
); | ||
} | ||
|
||
function transpile(source: string, map: any, filename: string) { | ||
const file = new babel.File( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What happens if the user doesn't have There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It uses normal As for inlining config in |
||
{ | ||
filename, | ||
sourceMaps: true, | ||
inputSourceMap: map, | ||
}, | ||
new babel.Pipeline() | ||
); | ||
|
||
// `transformFromAst` is synchronous in Babel 6, but async in Babel 7 hence | ||
// the `transformFromAstSync`. | ||
return (babel.transformFromAstSync || babel.transformFromAst)( | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Probably better to use the async There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. There is no async version of There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yes, I meant always use |
||
file.parse(source), | ||
source, | ||
{ | ||
filename, | ||
sourceMaps: true, | ||
inputSourceMap: map, | ||
presets: [require.resolve('../../babel.js')], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. We should be able to pass options to our babel preset via the loader options There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Done There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hmm, shouldn't it be There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why? There are other options for babel that you might be interested in. Limiting it only to presets doesn't sound good. What if you want to pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Still, if you want to pass options to linaria preset it would look like this: {
loader: 'linaria/loader',
options: {
presets: [['linaria/babel', { /* options */ }]]
}
} There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why would we want to allow passing arbitrary presets and plugins? It's There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. But maybe you don't want all of out plugins to be applied? Only There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure what's the usecase, but it should be an item in the options object if it needs to be exposed instead of overriding all plugins/presets. |
||
parserOpts: file.parserOpts, | ||
babelrc: false, | ||
} | ||
); | ||
} | ||
|
||
function getLinariaParentModules(fs: any, module: any) { | ||
const parentModules = []; | ||
|
||
function findLinariaModules(reasons) { | ||
reasons.forEach(reason => { | ||
if (!reason.module.resource) { | ||
return; | ||
} | ||
|
||
const source = fs.readFileSync(reason.module.resource).toString(); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Better to do There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. It supports it, but is there any actual reason to change it? |
||
if (shouldRunLinaria(source)) { | ||
parentModules.push({ source, filename: reason.module.resource }); | ||
} | ||
|
||
findLinariaModules(reason.module.reasons); | ||
}); | ||
} | ||
|
||
findLinariaModules(module.reasons); | ||
|
||
return parentModules; | ||
} | ||
|
||
const builtLinariaModules = []; | ||
|
||
function linariaLoader(source: string, inputMap: any, meta: any) { | ||
// If the module has linaria styles, we build it and we're done here. | ||
if (shouldRunLinaria(source)) { | ||
const { code, map } = transpile(source, inputMap, this.resourcePath); | ||
builtLinariaModules.push(this.resourcePath); | ||
return { | ||
source: code, | ||
map, | ||
meta, | ||
}; | ||
} | ||
|
||
// Otherwise, we check for parent modules, which use this one | ||
// and if they have linaria styles, we build them. | ||
const parentModuleToTranspile = getLinariaParentModules( | ||
this.fs.fileSystem, | ||
this._module | ||
); | ||
|
||
parentModuleToTranspile.forEach(item => { | ||
// We only care about modules which was previously built. | ||
if (builtLinariaModules.indexOf(item.filename) > -1) { | ||
transpile(item.source, null, item.filename); | ||
} | ||
}); | ||
|
||
return { | ||
source, | ||
map: inputMap, | ||
meta, | ||
}; | ||
} | ||
|
||
function makeLoaderAdapter(fn) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This abstraction seems unnecessary. Why not call the function directly instead of creating a higher order function to which you pass it as an argument? We don't seem to need it to reuse anywhere. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I got it done, before I found a perfect solution and back them the whole loader was async making it harder to integrate with webpack. |
||
function loaderAdapter(...args: any[]) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This code can be hugely simplified by making the result always async: const callback = this.async();
Promise.resolve()
.then(() => fn.call(this, ...args))
.then(
({ source, map, meta }) => callback(null, source, map, meta),
error => callback(error)
); In this case, it's better to use the second argument of |
||
let error; | ||
let results; | ||
try { | ||
results = fn.call(this, ...args); | ||
} catch (e) { | ||
error = e; | ||
} | ||
|
||
if (results && results instanceof Promise) { | ||
const callback = this.async(); | ||
results | ||
.then(({ source, map, meta }) => { | ||
callback(null, source, map, meta); | ||
}) | ||
.catch(e => { | ||
callback(e); | ||
}); | ||
} else if (results && !error) { | ||
this.callback(null, results.source, results.map, results.meta); | ||
} else { | ||
this.callback(error); | ||
} | ||
} | ||
|
||
Object.defineProperty(loaderAdapter, 'name', { | ||
value: fn.name || loaderAdapter.name, | ||
}); | ||
|
||
return loaderAdapter; | ||
} | ||
|
||
export default makeLoaderAdapter(linariaLoader); |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -50,7 +50,7 @@ module.exports = (env = { NODE_ENV: 'development' }) => ({ | |
{ | ||
test: /\.js$/, | ||
exclude: /node_modules/, | ||
use: { loader: 'babel-loader' }, | ||
use: [{ loader: 'babel-loader' }, { loader: 'linaria/loader' }], | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Let's enable cache for |
||
}, | ||
].concat( | ||
env.NODE_ENV === 'production' | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We want to publish this file, please add it to "files" in package.json
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done