Skip to content

Commit

Permalink
feat: add node API and cli
Browse files Browse the repository at this point in the history
  • Loading branch information
zamotany authored and satya164 committed Dec 16, 2018
1 parent 60174de commit 9e2e71a
Show file tree
Hide file tree
Showing 9 changed files with 205 additions and 57 deletions.
96 changes: 96 additions & 0 deletions bin/linaria.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,96 @@
#!/usr/bin/env node

/* @flow */

const path = require('path');
const fs = require('fs');
const mkdir = require('mkdirp');
const glob = require('glob');
const commander = require('commander');
const { transform } = require('../lib/node');

commander
// $FlowFixMe
.version(require(path.join(__dirname, '../package.json')).version)
.usage('[options] <file1> [<fileN>...]')
.option('-s, --source-maps', 'generate source maps')
.option('-r, --require-css', 'require CSS in original JS file')
.option('-o, --out-dir <dir>', 'output directory')
.action((file, ...rest) => {
if (typeof file !== 'string') {
commander.help();
}

const command = rest[rest.length - 1];
const files = [file, ...rest.slice(0, -1)];
// console.log({
// files,
// sourceMaps: Boolean(command.sourceMaps),
// requireCss: Boolean(command.requireCss),
// outDir: command.outDir,
// config: command.config,
// });
processFiles(files, {
sourceMaps: Boolean(command.sourceMaps),
requireCss: Boolean(command.requireCss),
outDir: command.outDir || '.',
});
})
.parse(process.argv);

/* ::
type Options = {
sourceMaps: boolean,
requireCss: boolean,
outDir: string,
};
*/

function processFiles(files /* :string[] */, options /* :Options */) {
const resolvedFiles = files.reduce(
(acc, pattern) => [...acc, ...glob.sync(pattern, { absolute: true })],
[]
);

resolvedFiles.forEach(filename => {
const outputFilename = resolveOutputFilename(filename, options.outDir);
// const { cssText, sourceMap, cssSourceMapText } = transform(
transform(fs.readFileSync(filename).toString(), {
inputFilename: filename,
outputFilename,
inputSourceMap: null,
pluginOptions: {},
});

// if (cssText) {
// mkdir.sync(path.dirname(outputFilename));
// const cssContent =
// options.sourceMaps && sourceMap
// ? `${cssText}\n/*# sourceMappingURL=${outputFilename}.map */`
// : cssText;
// fs.writeFileSync(outputFilename, cssContent);
// if (options.sourceMaps && sourceMap) {
// // $FlowFixMe
// fs.writeFileSync(`${outputFilename}.map`, cssSourceMapText);
// }
// if (options.requireCss) {
// fs.writeFileSync(
// filename,
// `${fs.readFileSync(filename).toString()}\nrequire('${path.relative(
// path.dirname(filename),
// outputFilename
// )}');`
// );
// }

console.log(`Writing ${outputFilename}`);
});
}

function resolveOutputFilename(filename /* :string */, outDir /* :string */) {
const folderStructure = path.relative(process.cwd(), path.dirname(filename));
const outputBasename = path
.basename(filename)
.replace(path.extname(filename), '.css');
return path.resolve(outDir, folderStructure, outputBasename);
}
1 change: 1 addition & 0 deletions node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
module.exports = require('./lib/node');
12 changes: 9 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
"version": "1.0.0-beta.1",
"description": "Blazing fast zero-runtime CSS in JS library",
"main": "lib/index.js",
"bin": "./bin/linaria.js",
"files": [
"lib/",
"react.js",
Expand Down Expand Up @@ -40,6 +41,7 @@
"add-contributor": "all-contributors add",
"prebuild": "del lib",
"build": "babel src --out-dir lib --ignore '**/__tests__/**,**/__integration-tests__/**,**/__fixtures__/**' --source-maps && flow-copy-source -i '{**/__tests__/**,**/__integration-tests__/**,**/__fixtures__/**}' src lib",
"watch": "babel src --out-dir lib --ignore '**/__tests__/**,**/__integration-tests__/**,**/__fixtures__/**' --source-maps --watch",
"prepare": "yarn build",
"release": "release-it",
"now-build": "yarn --cwd website install && yarn --cwd website build",
Expand Down Expand Up @@ -82,13 +84,17 @@
"@babel/plugin-syntax-dynamic-import": "^7.0.0",
"@babel/plugin-transform-modules-commonjs": "^7.1.0",
"@babel/register": "^7.0.0",
"commander": "^2.19.0",
"dedent": "^0.7.0",
"glob": "^7.1.3",
"loader-utils": "^1.1.0",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"postcss": "^7.0.5",
"react-is": "^16.5.2",
"postcss": "^7.0.2",
"react-is": "^16.5.1",
"rollup-pluginutils": "^2.3.3",
"source-map": "^0.7.3",
"stylis": "^3.5.3"
"stylis": "^3.5.4"
},
"resolutions": {
"**/babel-core": "7.0.0-bridge.0"
Expand Down
22 changes: 14 additions & 8 deletions src/loader.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,17 @@
/* @flow */

const fs = require('fs');
const path = require('path');
const mkdirp = require('mkdirp');
// $FlowFixMe
const Module = require('module');
const loaderUtils = require('loader-utils');
const transform = require('./transform');

module.exports = function loader(content, inputSourceMap) {
module.exports = function loader(
content /* :string */,
inputSourceMap /* :Object | void */
) {
const { sourceMap, cacheDirectory = '.linaria-cache', ...rest } =
loaderUtils.getOptions(this) || {};

Expand All @@ -19,20 +25,20 @@ module.exports = function loader(content, inputSourceMap) {
)
);

const result = transform(
this.resourcePath,
content,
rest,
const result = transform(content, {
inputFilename: this.resourcePath,
inputSourceMap,
outputFilename
);
outputFilename,
// $FlowFixMe
pluginOptions: rest,
});

if (result.cssText) {
let { cssText } = result;

if (sourceMap) {
cssText += `/*# sourceMappingURL=data:application/json;base64,${Buffer.from(
result.cssSourceMapText
result.cssSourceMapText || ''
).toString('base64')}*/`;
}

Expand Down
3 changes: 3 additions & 0 deletions src/node.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
/* @flow */

module.exports.transform = require('./transform');
7 changes: 6 additions & 1 deletion src/rollup.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,12 @@ module.exports = function linaria({
transform(code /* :string */, id /* :string */) {
if (!filter(id)) return;

const result = transform(id, code, rest);
const result = transform(code, {
inputFilename: id,
outputFilename: null,
inputSourceMap: null,
pluginOptions: rest,
});

if (!result.cssText) return;

Expand Down
9 changes: 5 additions & 4 deletions src/stylelint/preprocessor.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,14 @@ function preprocessor() {

return {
code(input /* : string */, filename /* : string */) {
/* eslint-disable prefer-destructuring */

let result;

try {
result = transform(filename, input, {
evaluate: true,
result = transform(input, {
inputFilename: filename,
pluginOptions: {
evaluate: true,
},
});
} catch (e) {
// Ignore parse errors
Expand Down
71 changes: 43 additions & 28 deletions src/transform.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,45 +40,58 @@ type PluginOptions = {
}
*/

/* ::
type Options = {
inputFilename: string,
outputFilename?: string,
inputSourceMap?: Object,
pluginOptions?: {
evaluate?: boolean,
displayName?: boolean,
},
}
*/

const STYLIS_DECLARATION = 1;

module.exports = function transform(
filename /* :string */,
content /* :string */,
options /* :PluginOptions */,
inputSourceMap /* :?Object */,
outputFilename /* : ?string */
code /* :string */,
options /* :Options */
) /* : Result */ {
// Check if the file contains `css` or `styled` words first
// Otherwise we should skip transforming
if (!/\b(styled|css)/.test(content)) {
if (!/\b(styled|css)/.test(code)) {
return {
code: content,
sourceMap: inputSourceMap,
code,
sourceMap: options.inputSourceMap,
};
}

// Parse the code first so babel uses user's babel config for parsing
// We don't want to use user's config when transforming the code
const ast = babel.parseSync(content, {
filename,
const ast = babel.parseSync(code, {
filename: options.inputFilename,
caller: { name: 'linaria' },
});

const { metadata, code, map } = babel.transformFromAstSync(ast, content, {
filename,
presets: [[require.resolve('./babel'), options]],
babelrc: false,
configFile: false,
sourceMaps: true,
sourceFileName: filename,
inputSourceMap,
});
const { metadata, code: transformedCode, map } = babel.transformFromAstSync(
ast,
code,
{
filename: options.inputFilename,
presets: [[require.resolve('./babel'), options.pluginOptions]],
babelrc: false,
configFile: false,
sourceMaps: true,
sourceFileName: options.inputFilename,
inputSourceMap: options.inputSourceMap,
}
);

if (!metadata.linaria) {
return {
code: content,
sourceMap: inputSourceMap,
code,
sourceMap: options.inputSourceMap,
};
}

Expand All @@ -88,7 +101,7 @@ module.exports = function transform(
let cssText = '';

stylis.use(null)((context, decl) => {
if (context === STYLIS_DECLARATION && outputFilename) {
if (context === STYLIS_DECLARATION && options.outputFilename) {
// When writing to a file, we need to adjust the relative paths inside url(..) expressions
// It'll allow css-loader to resolve an imported asset properly
return decl.replace(
Expand All @@ -98,9 +111,9 @@ module.exports = function transform(
// Replace asset path with new path relative to the output CSS
path.relative(
/* $FlowFixMe */
path.dirname(outputFilename),
path.dirname(options.outputFilename),
// Get the absolute path to the asset from the path relative to the JS file
path.resolve(path.dirname(filename), p2)
path.resolve(path.dirname(options.inputFilename), p2)
) +
p3
);
Expand All @@ -124,7 +137,7 @@ module.exports = function transform(
});

return {
code,
code: transformedCode,
cssText,
rules,
replacements,
Expand All @@ -134,14 +147,16 @@ module.exports = function transform(
get cssSourceMapText() {
if (mappings && mappings.length) {
const generator = new SourceMapGenerator({
file: filename.replace(/\.js$/, '.css'),
file: options.inputFilename.replace(/\.js$/, '.css'),
});

mappings.forEach(mapping =>
generator.addMapping(Object.assign({}, mapping, { source: filename }))
generator.addMapping(
Object.assign({}, mapping, { source: options.inputFilename })
)
);

generator.setSourceContent(filename, content);
generator.setSourceContent(options.inputFilename, code);

return generator.toString();
}
Expand Down
Loading

0 comments on commit 9e2e71a

Please sign in to comment.