Skip to content

Commit

Permalink
feat: use separate module system to properly handle errors (#111)
Browse files Browse the repository at this point in the history
* use custom module system to generate proper stacktrace

* fixes and tweaks

* use snapshots only to check errors

* tweak test
  • Loading branch information
zamotany authored and satya164 committed Sep 21, 2017
1 parent 08b6425 commit 1c61aa4
Show file tree
Hide file tree
Showing 37 changed files with 2,857 additions and 1,172 deletions.
2 changes: 1 addition & 1 deletion .babelrc
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"browsers": ["last 2 versions"]
}
}],
"stage-3"
"stage-2"
],
"plugins": [
"transform-flow-strip-types"
Expand Down
2 changes: 2 additions & 0 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ aliases:
version: 2
jobs:
build:
working_directory: '~/linaria'
docker:
- image: circleci/node:6.10.3
steps:
Expand All @@ -37,6 +38,7 @@ jobs:
- run: yarn run lint
- run: yarn run flow
- run: yarn test -- --coverage
- run: yarn run test:integration:ci
- run: cat ./coverage/lcov.info | ./node_modules/.bin/codecov
- store_artifacts:
path: coverage
Expand Down
24 changes: 14 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,12 +15,14 @@
"scripts": {
"flow": "flow",
"lint": "eslint .",
"test": "jest",
"test": "jest __tests__",
"test:integration": "yarn run build:clean && yarn run build:transpile && yarn run test:integration:ci",
"test:integration:ci": "jest __integration-tests__",
"prepare": "yarn run build:clean && yarn run build:transpile && yarn run build:flow",
"build:clean": "del build",
"build:transpile": "babel src --out-dir build --ignore '**/__tests__/**,**/__mocks__/**,**/__fixtures__/**'",
"build:transpile:watch": "babel src --out-dir build --watch --ignore '**/__tests__/**,**/__mocks__/**,**/__fixtures__/**'",
"build:flow": "flow-copy-source src build -i '**/__tests__/**'",
"build:transpile": "babel src --out-dir build --ignore '**/__tests__/**,**/__integration-tests__/**,**/__mocks__/**,**/__fixtures__/**'",
"build:transpile:watch": "babel src --out-dir build --watch --ignore '**/__tests__/**,**/__integration-tests__/**,**/__mocks__/**,**/__fixtures__/**'",
"build:flow": "flow-copy-source src build -i '**/__tests__/**,**/__integration-tests__/**,**/__mocks__/**,**/__fixtures__/**'",
"benchmark": "yarn run prepare && node ./benchmark/run.js",
"postinstall": "yarn link && yarn link linaria"
},
Expand All @@ -30,36 +32,38 @@
"server.js"
],
"dependencies": {
"babel-code-frame": "^6.26.0",
"babel-core": "^6.26.0",
"babel-generator": "^6.26.0",
"babel-plugin-preval": "^1.4.4",
"babel-polyfill": "^6.26.0",
"babel-register": "^6.26.0",
"dedent": "^0.7.0",
"error-stack-parser": "^2.0.1",
"postcss": "^6.0.10",
"source-map": "^0.5.7",
"stylis": "^3.2.13"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-plugin-transform-flow-strip-types": "^6.22.0",
"babel-preset-env": "1.6.0",
"babel-preset-es2015": "^6.24.1",
"babel-preset-stage-3": "^6.24.1",
"babel-preset-stage-2": "^6.24.1",
"benchmark": "^2.1.4",
"cli-table": "^0.3.1",
"codecov": "^2.3.0",
"cssom": "^0.3.2",
"del-cli": "^1.1.0",
"escape-string-regexp": "^1.0.5",
"eslint": "^4.6.1",
"eslint-config-callstack-io": "^0.4.1",
"flow-bin": "^0.53.1",
"flow-copy-source": "^1.2.1",
"glob": "^7.1.2",
"jest": "^21.1.0",
"prettier": "^1.6.1"
"prettier": "^1.6.1",
"strip-ansi": "^4.0.0"
},
"jest": {
"testRegex": "/__tests__/.*\\.(test|spec)\\.js$",
"testRegex": "/__(integration-)?tests__/.*\\.(test|spec)\\.js$",
"testEnvironment": "node"
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,4 +6,7 @@ module.exports = {
add5(value) {
return value + 5;
},
throw() {
throw new Error('test');
},
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`preval-extract babel plugin should build valid code frame for errors while evaling styles 1`] = `
Command failed: node <<REPLACED>>/src/babel/__integration-tests__/__utils__/transpile.js '"const test = () => {\\n throw new Error(\\"Some weird error\\");\\n};\\n\\nfunction m() {\\n test();\\n}\\n\\nconst header = css\`color: \${m()};\`;"' '{"pluginOptions":{"cache":false,"extract":false},"babelOptions":{"filename":"test.js"}}'
<<REPLACED>>/node_modules/babel-core/lib/transformation/file/index.js:590
throw err;
^
Error: Some weird error
1 | import css from '<<REPLACED>>/build/css.js';
2 |
3 | var test = () => {
> 4 | throw new Error("Some weird error");
| ^
5 | }
6 |
7 | function m() {
at <<REPLACED>>/test.js:4:6
`;
exports[`preval-extract babel plugin should build valid code frame for errors while evaling styles 2`] = `
Command failed: node <<REPLACED>>/src/babel/__integration-tests__/__utils__/transpile.js '"const utils = require(\\"./src/babel/__integration-tests__/__fixtures__/commonjs/utils.js\\");\\n\\nconst header = css\`color: \${utils.throw()};\`;"' '{"pluginOptions":{"cache":false,"extract":false},"babelOptions":{"filename":"test.js"}}'
<<REPLACED>>/node_modules/babel-core/lib/transformation/file/index.js:590
throw err;
^
Error: test
6 | add5(value) {
7 | return value + 5;
8 | },
9 | throw() {
> 10 | throw new Error('test');
| ^
11 | },
12 | };
13 |
at <<REPLACED>>/src/babel/__integration-tests__/__fixtures__/commonjs/utils.js:10:10
at <<REPLACED>>/test.js:5:46
`;
exports[`preval-extract babel plugin should create classname for "css" tagged template literal 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin should create classname for "css.named()" tagged template literal 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin should create classname for non-top-level "css" tagged template literal 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin should create classnames for multiple "css" tagged template literal 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin should create classnames for multiple "css" tagged template literal 2`] = `"border-radius: 4px;"`;
exports[`preval-extract babel plugin should not process tagged template if tag is not "css" 1`] = `
'use strict';
var _css = require('<<REPLACED>>/build/css.js');
var _css2 = _interopRequireDefault(_css);
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var header = '\\nfont-size: 3em;\\n';
`;
exports[`preval-extract babel plugin should preval const and let without transpilation to var 1`] = `
import css from '<<REPLACED>>/build/css.js';
const size = 3;
let color = "#ffffff";
/*linaria-output*/const header = "header__3r30ar";
`;
exports[`preval-extract babel plugin should preval css with classname from another prevaled css 1`] = `
"padding: 2rem;
.title__1r77qux {
margin-bottom: 1rem;
}"
`;
exports[`preval-extract babel plugin should throw error if the id was not found 1`] = `
Command failed: node <<REPLACED>>/src/babel/__integration-tests__/__utils__/transpile.js '"const title = css\`\\n width: \${document.width};\\n\`;"' '{"pluginOptions":{"cache":false,"extract":false},"babelOptions":{"filename":"test.js"}}'
<<REPLACED>>/node_modules/babel-core/lib/transformation/file/index.js:590
throw err;
^
SyntaxError: test.js: Linaria css evaluation error:
Could not find a reference to 'document'.
This might happen if you used some undeclared variable/function or a browser specific API.
2 |
3 | const title = css\`
> 4 | width: \${document.width};
| ^
5 | \`;
at File.buildCodeFrameError (<<REPLACED>>/node_modules/babel-core/lib/transformation/file/index.js:427:15)
at NodePath.buildCodeFrameError (<<REPLACED>>/node_modules/babel-traverse/lib/path/index.js:140:26)
at resolveSource (<<REPLACED>>/build/babel/preval-extract/resolveSource.js:33:16)
at Object.Identifier (<<REPLACED>>/build/babel/preval-extract/index.js:46:48)
at NodePath._call (<<REPLACED>>/node_modules/babel-traverse/lib/path/context.js:76:18)
at NodePath.call (<<REPLACED>>/node_modules/babel-traverse/lib/path/context.js:48:17)
at NodePath.visit (<<REPLACED>>/node_modules/babel-traverse/lib/path/context.js:105:12)
at TraversalContext.visitQueue (<<REPLACED>>/node_modules/babel-traverse/lib/context.js:150:16)
at TraversalContext.visitSingle (<<REPLACED>>/node_modules/babel-traverse/lib/context.js:108:19)
at TraversalContext.visit (<<REPLACED>>/node_modules/babel-traverse/lib/context.js:192:19)
`;
exports[`preval-extract babel plugin with ES imports should preval default export 1`] = `"font-size: 14px;"`;
exports[`preval-extract babel plugin with ES imports should preval imported module tree with constants 1`] = `"font-size: 28px;"`;
exports[`preval-extract babel plugin with ES imports should preval named imports 1`] = `"font-size: 36px;"`;
exports[`preval-extract babel plugin with ES imports should preval named imports 2`] = `"font-size: 24px;"`;
exports[`preval-extract babel plugin with commonjs imports should preval imported constants 1`] = `"font-size: 14px;"`;
exports[`preval-extract babel plugin with commonjs imports should preval imported constants with destructurization 1`] = `"font-size: 14px;"`;
exports[`preval-extract babel plugin with extraction enabled extract styles to a given file 1`] = `"/* THIS FILE IS AUTOGENERATED. DO NOT EDIT IT DIRECTLY, NOR COMMIT IT TO VERSION CONTROL. */"`;
exports[`preval-extract babel plugin with extraction enabled extract styles to a given file with output directory specified 1`] = `"/* THIS FILE IS AUTOGENERATED. DO NOT EDIT IT DIRECTLY, NOR COMMIT IT TO VERSION CONTROL. */"`;
exports[`preval-extract babel plugin with extraction enabled should extract all styles to a single file 1`] = `
"/* THIS FILE IS AUTOGENERATED. DO NOT EDIT IT DIRECTLY, NOR COMMIT IT TO VERSION CONTROL. */
.header__nzd8dy{font-size:3em;}"
`;
exports[`preval-extract babel plugin with extraction enabled should extract all styles to a single file 2`] = `
"/* THIS FILE IS AUTOGENERATED. DO NOT EDIT IT DIRECTLY, NOR COMMIT IT TO VERSION CONTROL. */
.body__j2fydj{font-weight:bold;}"
`;
exports[`preval-extract babel plugin with extraction enabled should extract each style to separate file and include it into source file 1`] = `
"/* THIS FILE IS AUTOGENERATED. DO NOT EDIT IT DIRECTLY, NOR COMMIT IT TO VERSION CONTROL. */
.header__nzd8dy{font-size:3em;}"
`;
exports[`preval-extract babel plugin with extraction enabled should extract each style to separate file and include it into source file 2`] = `
"/* THIS FILE IS AUTOGENERATED. DO NOT EDIT IT DIRECTLY, NOR COMMIT IT TO VERSION CONTROL. */
.body__j2fydj{font-weight:bold;}"
`;
exports[`preval-extract babel plugin with function calls should preval multiple function calls inside an expression 1`] = `"font-size: 33px;"`;
exports[`preval-extract babel plugin with function calls should preval with function call inside an expression 1`] = `"font-size: 28px;"`;
exports[`preval-extract babel plugin with function delcarations/expressions should preval function with flat/shallow external ids 1`] = `"font-size: 14px;"`;
exports[`preval-extract babel plugin with function delcarations/expressions should preval function with multiple nested/deep external ids 1`] = `"font-size: 28px;"`;
exports[`preval-extract babel plugin with function delcarations/expressions should preval function with nested/deep external ids 1`] = `"font-size: 15px;"`;
exports[`preval-extract babel plugin with function delcarations/expressions should preval with arrow function 1`] = `"font-size: 14px;"`;
exports[`preval-extract babel plugin with function delcarations/expressions should preval with function declaration 1`] = `"font-size: 14px;"`;
exports[`preval-extract babel plugin with function delcarations/expressions should preval with function expression 1`] = `"font-size: 14px;"`;
exports[`preval-extract babel plugin with plain objects should preval styles with deeply destructurized object 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin with plain objects should preval styles with deeply destructurized object and aliases 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin with plain objects should preval styles with deeply destructurized object, aliases and defaults 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin with plain objects should preval styles with nested object 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin with plain objects should preval styles with shallow object 1`] = `"font-size: 3em;"`;
exports[`preval-extract babel plugin with plain objects should preval styles with shallowly destructurized object 1`] = `"font-size: 3em;"`;
55 changes: 55 additions & 0 deletions src/babel/__integration-tests__/__utils__/exec.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
/* @flow */

import childProcess from 'child_process';
import path from 'path';

export function transpile(
source: string,
pluginOptions: any = { cache: false, extract: false },
babelOptions: any = { filename: 'test.js' }
) {
const PATH_TO_TRANSPILE_BIN = path.join(__dirname, './transpile.js');

const stdout = childProcess.execSync(
`node ${PATH_TO_TRANSPILE_BIN} '${JSON.stringify(
source
)}' '${JSON.stringify({
pluginOptions,
babelOptions,
})}'`,
{ stdio: 'pipe' }
);

const [transpiled, rawStyles] = stdout.toString().split('\n');
const styles = JSON.parse(rawStyles || '{}');
return {
code: JSON.parse(transpiled || '""'),
styles,
getCSSForClassName(className: string) {
return styles[`.${className}`];
},
};
}

export function extract(
source: string,
pluginOptions: any = { cache: false, extract: true },
babelOptions: any = { filename: 'test.js' }
) {
const PATH_TO_EXTRACT_BIN = path.join(__dirname, './extract.js');

const stdout = childProcess.execSync(
`node ${PATH_TO_EXTRACT_BIN} '${JSON.stringify(source)}' '${JSON.stringify({
pluginOptions,
babelOptions,
})}'`,
{ stdio: 'pipe' }
);

const [data, filenames, transpiled] = stdout.toString().split('\n');
return {
data: JSON.parse(data || '""'),
filenames: JSON.parse(filenames || '[]'),
transpiled: JSON.parse(transpiled || '""'),
};
}
26 changes: 26 additions & 0 deletions src/babel/__integration-tests__/__utils__/extract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
const fs = require('fs');

const writeFileSync = fs.writeFileSync;

let buffer = '';
const filenames = [];

fs.writeFileSync = (filename, data, opts) => {
if (/\.css$/.test(filename)) {
filenames.push(filename);
buffer += data;
return;
}
writeFileSync(filename, data, opts);
};

const transpile = require('./transpile');

const code = transpile(
JSON.parse(process.argv[2]),
JSON.parse(process.argv[3])
);

console.log(JSON.stringify(buffer));
console.log(JSON.stringify(filenames));
console.log(JSON.stringify(code || ''));
Loading

0 comments on commit 1c61aa4

Please sign in to comment.