Migrating from Babel to SWC with React - javascript

TL;DR
How to translate a node script like this:
"test": "NODE_ENV=test riteway -r #babel/register 'src/**/*.test.js' | tap-nirvana",
to use SWC instead of Babel?
Context
We recently upgraded our Next.js version. Next.js now supports SWC instead of Babel.
The unit tests for React in our project are written with RITEway. The test command is:
"test": "NODE_ENV=test riteway -r #babel/register 'src/**/*.test.js' | tap-nirvana",
It transforms the files with Babel first because otherwise import statements and JSX would cause errors.
During our attempts to migrating to SWC, we had no luck with the CLI. However, we found the #swc-node/register package. It allowed us to transform our command like this:
"test": "riteway -r #swc-node/register src/**/*.test.js | tap-nirvana"
which fixes new syntax like the import statement and a test like this would run:
import { describe } from 'riteway';
describe('my test', async assert => {
assert({
given: 'true',
should: 'be true',
actual: true,
expected: true,
});
});
However, tests for React components like this
import { describe } from 'riteway';
import render from 'riteway/render-component';
import Authentication from './user-authentication-component';
describe('user authentication component', async assert => {
const $ = render(<Authentication />);
assert({
given: 'just rendering',
should: "render 'Authentication'",
actual: $('*:contains("Authentication")').text(),
expected: 'Authentication',
});
});
still throw the following error:
$ yarn test
yarn run v1.22.17
$ riteway -r #swc-node/register src/**/*.test.js | tap-nirvana
/Users/user/dev/learning-flow/node_modules/#swc/core/index.js:135
return bindings.transformSync(isModule ? JSON.stringify(src) : src, isModule, toBuffer(newOptions));
^
Error: error: Expression expected
|
7 | const $ = render(<Authentication />);
| ^
error: Expression expected
|
7 | const $ = render(<Authentication />);
| ^
error: Unexpected token `)`. Expected this, import, async, function, [ for array literal, { for object literal, # for decorator, function, class, null, true, false, number, bigint, string, regexp, ` for template literal, (, or an identifier
|
7 | const $ = render(<Authentication />);
| ^
Caused by:
0: failed to process js file
1: Syntax Error
at Compiler.transformSync
How can we create that command so that it runs with SWC?

After some experimentation I found out that it works if you import React from 'react'; in every file (both the component as well as the test) and change the file extensions to .jsx as described in the docs.
However, this is unsatisfying, as we'd like to use React 17's JSX transform feature to avoid having to import React in every file. Additionally, we'd like to keep all file endings .js.
We tried setting .swcrc without any luck so far.
I'm posting this answer here in case no way to configure .swcrc can be found.

I'll assume your question is only about jest and not about the webpack setup for swc.
I've never used swc myself in jest so this is just a shot in the dark, but I found a package for jest called #swc-node/jest which allows you to use a transformer like:
module.exports = {
transform: {
'^.+\\.(t|j)sx?$': ['#swc-node/jest'],
},
}
Which should allow you to transpile all [jt]sx? imports.
There's also a different one called #swc/jest which seems to do the same thing.
I'd start with trying those.
Your issue is that jest isn't able to parse your code, for example if you were using typescript you'd need something like ts-jest in order to parse your TS for you, I think in this case you need a swc/jest transpiler which should first compile your code and output it to memory, then funnel it to jest to run it.
The alternative is that you could compile the tests before hand using swc, then run jest on the compiled files as a separate step. At least with TS you can do that, first compile everything using tsc and then run jest on the .js output. I'm sure swc should work the same.
As to why the #swc-node/register package you're using isn't working, I have no idea.

Related

Mocha / Webpack: Cannot use import statement outside a module

I'm new to Node and JS Testing. I have a web applications w/ Webpack as a bundler. I have some entry point JS's which are included into the page. The entry points are using module files like this:
export default function() {
...
}
Now I would like to Unit test this module. I have picked up Mocha but it is not critical to me. Could be Jest or anything else.
I wrote a very simple test.js like this. It it not doing anything but tests if the entire setup works:
import foo from '../js/someModuleOfMine'
const assert = require('assert')
describe('Test Suite', () => {
it('should at least run', () => {
assert.equal(true, true)
})
})
executing mocha from CLI gives me this error:
import foo from '../js/someModuleOfMine'
^^^^^^
SyntaxError: Cannot use import statement outside a module
Adhering to some advises I have tired to add "type": "module" to my package.json but it only changed error to something even more obscure:
Error: Not supported
I am definitely missing something obvious but I cannot comprehend what.
Not sure if it will help the OP, but I had the same problem and it was due to typescript. I solved in this way:
install ts-node:
npm install ts-node --save-dev
add the require line in the mocha config (I have it in the package.json, but one can also have it in the .mocharc.json file):
"mocha": {
"spec": "./**/*test.ts",
"ignore": "./node_modules/**",
"require": "ts-node/register/files",
"timeout": 20000
}

Ignore return outside of function with babel 7

I recently updated to babel 7 and webpack 4 and am receiving this error when running our gulp build task:
gulp build
[00:26:04] Requiring external module #babel/register
[91m[BABEL] Note: The code generator has deoptimised the styling of /node_modules/lodash/lodash.js as it exceeds the max of 500KB.
[0m[91m/node_modules/#babel/core/lib/parser/index.js:95
throw err;
^
SyntaxError: /node_modules/dev-ip/lib/dev-ip.js: 'return' outside of function (41:8)
39 | var out = getIp();
40 | if (!out.length) {
> 41 | return console.log(messages.error);
| ^
42 | }
43 | console.log(getIp("cli"));
44 | }
at Parser.raise (/node_modules/#babel/parser/src/parser/location.js:41:63)
at Parser.parseReturnStatement (/node_modules/#babel/parser/src/parser/statement.js:577:12)
at Parser.parseStatementContent (/node_modules/#babel/parser/src/parser/statement.js:199:21)
at Parser.parseStatement (/node_modules/#babel/parser/src/parser/statement.js:146:17)
at Parser.parseBlockOrModuleBlockBody (/node_modules/#babel/parser/src/parser/statement.js:865:25)
at Parser.parseBlockBody (/node_modules/#babel/parser/src/parser/statement.js:841:10)
at Parser.parseBlock (/node_modules/#babel/parser/src/parser/statement.js:818:10)
at Parser.parseStatementContent (/node_modules/#babel/parser/src/parser/statement.js:223:21)
at Parser.parseStatement (/node_modules/#babel/parser/src/parser/statement.js:146:17)
at Parser.parseIfStatement (/node_modules/#babel/parser/src/parser/statement.js:570:28)
[0m[91merror Command failed with exit code 1.
This is caused by the return outside of a function in browser-syncs dev-ip dependency.
Is there a way to configure my .babelrc file to ignore this?
I've tried the following:
Installing only production dependencies, but because browser sync is imported in my gulp file it's still being compiled
Setting up workspaces with yarn, but similar issue as #1
Dynamically importing browser sync in my gulp file, I guess this is not yet supported yet?
Telling babel to ignore or exclude compiling the node_modules folder, but this doesn't seem to do anything?
Apparently babel-parser has an option allowReturnOutsideFunction: true, but I can't figure out how to set this in my .babelrc file.
Any thoughts on how to get around this?
With Babel7, you can provide all parser options (which include allowReturnOutsideFunction) via parserOpts, e.g.:
// .babelrc.js
module.exports = {
parserOpts: { allowReturnOutsideFunction: true }
};
Since I could not find a solution to this, I ended up just forking browser-sync and dev-ip.
I give you, browser-stink

How do I correctly configure mocha tests with ts_transformer_keys?

I can't seem to set the custom transformer for ts-transform-keys with my mocha tests.
I’m using mocha 6.1.4
ts-node 8.3.0 https://www.npmjs.com/package/ts-node
ts-trasnformer-keys 0.3.5 https://github.com/kimamula/ts-transformer-keys
ttypescript 1.5.7 https://github.com/cevek/ttypescript
The ts-node documentation says that you cannot set a custom transformer on the CLI, only programatically. So I'm trying to use ttypescript to get around that restriction.
I've tried the following...
Note: test.ts contains the following
import { keys } from 'ts-transformer-keys';
describe("xyz"), () => {
it("123", (done) => {
keys<CustomInterface>();
});
});
Attempt 1) - Set the ts-node with an environment variable
TS_NODE_COMPILER="ttypescript" mocha test/test.ts --require ts-node/register
Then I have the following in test/tsconfig.json
{
"compilerOptions": {
"plugins": [
{ "transform": "ts-transformer-keys/transformer" }
]
}
}
This results in Uncaught TypeError: ts_transformer_keys_1.keys is not a function which indicates that the custom transformer wasn't used at compile time.
Attempt 2) Following the typescript API example from ts-transformer-keys
I added a mocha.opts file with the following
--file test/transformer-config.js
and a transformer-config.js file with the following
const ts = require('typescript');
const keysTransformer = require('ts-transformer-keys/transformer').default;
const program = ts.createProgram(['test/test.ts'], {
strict: true,
noEmitOnError: true,
target: ts.ScriptTarget.ES5
});
const transformers = {
before: [keysTransformer(program)],
after: []
};
const { emitSkipped, diagnostics } = program.emit(undefined, undefined, undefined, false, transformers);
if (emitSkipped) {
throw new Error(diagnostics.map(diagnostic => diagnostic.messageText).join('\n'));
}
Then I invoke it like this mocha test/test.ts --require ts-node/register
This results in the following error
/Users/jjohnson/Documents/OCS/hmp/git/hmp-server/server/test/ttypescript-register.js:17
throw new Error(diagnostics.map(diagnostic => diagnostic.messageText).join('\n'));
^
Error: [object Object]
[object Object]
[object Object]
at Object.<anonymous> (/Users/jjohnson/Documents/OCS/hmp/git/hmp-server/server/test/ttypescript-register.js:17:9)
at Module._compile (internal/modules/cjs/loader.js:777:30)
...
It feels like in Attempt 1 it wasn't ever calling the code that sets the custom transformer in tsconfig.json or if it was getting called the code was failing silently.
It feels like in Attempt 2 I'm creating a new instance of the typescript program and then that fails for some reason. And even if it succeeded I'm not sure that this is the right way to go about configuring things since the ts.createProgram wants a list of RootNames for the files it will transpile.
Maybe my entire approach is wrong.
All I really want is a way that in my mocha tests I can verify that the expected result type is what the method returned. And I'd like to be able to do this w/out touching too much of the source code.
you should be able to define your required module (see below) and run ts-node programmatically. In this way, you can safely use any customer transformer.
// tsnode.js
const transformer = require('ts-transformer-keys/transformer').default;
require("ts-node").register({
transformers: program => ({
before: [
transformer(program)
]
})
});
then you can run mocha with require
mocha --require './tsnode.js' --watch-extensions ts,tsx "test/**/*.{ts,tsx}
You can tell ts-node which compiler to use in tsconfig.json. This is covered in the ts-node docs. If your using transformers presumably your also using ttypescript compiler. You just need to add this:
"ts-node": {
"compiler": "ttypescript"
}

Jest - Jest encountered an unexpected token. Vanilla JS

I'm trying to make a complex zip extractor in JavaScript and I decided that unit testing would be very important. That being said, a friend recommended Jest. I couldn't get any of my tests to work so I made a dumb test that made sure the first value of my JS Enum is 0. Jest, however, fails ever time saying that it encountered an unexpected token
I tried a more complex test and simplified it down to this simple test:
enums.js:
const Format = {
UNKNOWN: 0,
ZIP: 1,
TAR_GZIP: 2,
TAR_BZIP: 3,
};
export default Format
enums.test.js
const {Format} = require("../src/enums.js");
test("bad test", () => {
expect(Format.UNKNOWN).toBe(0);
});
The error that is gives me is this:
Test suite failed to run
Jest encountered an unexpected token
This usually means that you are trying to import a file which Jest cannot parse, e.g. it's not plain JavaScript.
By default, if Jest sees a Babel config, it will use that to transform your files, ignoring "node_modules".
Here's what you can do:
• To have some of your "node_modules" files transformed, you can specify a custom "transformIgnorePatterns" in your config.
• If you need a custom transformation specify a "transform" option in your config.
• If you simply want to mock your non-JS modules (e.g. binary assets) you can stub them out with the "moduleNameMapper" config option.
You'll find more details and examples of these config options in the docs:
https://jestjs.io/docs/en/configuration.html
Details:
/home/giovanni/WebstormProjects/extract.js/src/enums.js:7
export default Format;
^^^^^^
SyntaxError: Unexpected token export
> 1 | const Format = require("../src/enums.js");
| ^
2 |
3 | test("bad test", () => {
4 | expect(Format.UNKNOWN).toBe(0);
at ScriptTransformer._transformAndBuildScript (node_modules/#jest/transform/build/ScriptTransformer.js:471:17)
at ScriptTransformer.transform (node_modules/#jest/transform/build/ScriptTransformer.js:513:25)
at Object.<anonymous> (test/enum.test.js:1:1)
Using Bable fixed this. Once I installed Babel with NPM, I added a file .babelrc with the contents:
{
"presets": ["#babel/preset-env"]
}
This worked.

React Native - unexpected token static propTypes when running assmbleRelease, babel plugin doesnt work

I'm trying to compile a release apk in React Native using either
"react-native run-android --variant=release" or
from ./android:
"./gradelw assmbleRelease"
I keep getting the following error:
> :app:bundleReleaseJsAndAssets
ERROR Failed to compile.
./node_modules/native-base-shoutem-theme/src/StyleProvider.js 10:19
Module parse failed: Unexpected token (10:19)
You may need an appropriate loader to handle this file type.
| */
| export default class StyleProvider extends React.Component {
> static propTypes = {
| children: PropTypes.element.isRequired,
...
I've installed this plugin:
https://babeljs.io/docs/en/next/babel-plugin-proposal-class-properties.html
and added it to my babel.config.js:
module.exports = {
presets: ['module:metro-react-native-babel-preset'],
plugins: [
'#babel/plugin-proposal-class-properties'
]
};
I've tried creating also a .babelrc file and setting it up. didn't work.
I tried configurating babel through package.json and also it didn't work.
I've tried doing
"react-native start -- --reset-cache"
but nothing helped. I keep getting the same error.
I know the babel.config.js is being read because if I mess around with it I see that the build fails for other reasons.
but a strange thing is that if I remove the file completely, I still get the original error (missing "static propTypes" thing).
why doesn't the plugin work?
Not sure why this happened, but removing "haul" did the trick.
"npm uninstall --save-dev haul"
and remove the added config in app/build.gradle:
project.ext.react = [
cliPath: "node_modules/haul/bin/cli.js"
]

Categories

Resources