I'm attempting to transpile Salesforce Commerce Cloud's .ds files to JavaScript so we can apply standard testing tools (Jest, Mocha, etc.). The SFCC docs indicate that .ds files are "Rhino JavaScript", with a non-standard extension for Flow-like type checking.
So far, stripping out the type annotations has been simple, using the transform-flow-strip-types plugin. But SFCC supports a deprecated "for each...in" statement from JavaScript 1.6 that Babel is choking on.
All code below is available on github.
Here's my source src/index.ds file:
function dump(a: Array) {
for each (var x in a) {
console.log(x);
}
}
module.exports = dump;
And my gulfile.js:
const gulp = require('gulp');
const babel = require('gulp-babel');
gulp.task('test', function () {
gulp.src('src/**/*.ds')
.pipe(babel())
.pipe(gulp.dest('dst'));
});
And this is my package.json:
{
"name": "dspoc",
"version": "1.0.0",
"description": "poc",
"main": "index.ds",
"author": "cjr",
"license": "ISC",
"devDependencies": {
"babel": "^6.23.0",
"babel-core": "^6.26.3",
"babel-plugin-transform-flow-strip-types": "^6.22.0",
"gulp": "^3.9.1",
"gulp-babel": "^7.0.1"
},
"babel": {
"plugins": [
"transform-flow-strip-types"
]
}
}
When I run gulp test, I get this:
%> gulp test
[11:23:06] Using gulpfile ~/dev/dspoc/gulpfile.js
[11:23:06] Starting 'test'...
[11:23:06] Finished 'test' after 9.15 ms
events.js:163
throw er; // Unhandled 'error' event
^
SyntaxError: /Users/craser/dev/dspoc/src/index.ds: Unexpected token, expected ( (2:5)
1 | function dump(a: Array) {
> 2 | for each (var x in a) {
| ^
3 | console.log(x);
4 | }
5 | }
at Parser.pp$5.raise (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:4454:13)
at Parser.pp.unexpected (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:1761:8)
at Parser.pp.expect (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:1749:33)
at Parser.pp$1.parseForStatement (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:2008:8)
at Parser.pp$1.parseStatement (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:1836:19)
at Parser.parseStatement (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:5910:22)
at Parser.pp$1.parseBlockBody (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:2268:21)
at Parser.pp$1.parseBlock (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:2247:8)
at Parser.pp$3.parseFunctionBody (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:4235:22)
at Parser.parseFunctionBody (/Users/craser/dev/dspoc/node_modules/babylon/lib/index.js:5897:20)
I've spent quite a bit of time digging for a plugin that will let Babel transpile this to something like the for...of statement, but I can't seem to find anything.
I'm now at the precipice of digging into the for-of transform was built and creating something similar to transform for each...in, but I really don't want to have to put that work in if I can avoid it.
I feel like I'm missing something obvious here. Anyone know how this can be done?
for each...in was never an official part of the spec and didn't exist by the time Babel came around, so it is not supported by Babel. I'm afraid you've have to update all usage of that syntax before Babel will be able to process it.
Related
I'm trying to lint JavaScript code with optional chaining syntax like:
let foo = bar?.property;
When parsing my JS files with eslint explicitly, it passes.
When parsing with gulp-eslint using the same configuration, linting fails with:
Parsing error: Unexpected token .
My .eslintrc.json file contains:
{
"parserOptions": {
"ecmaVersion": 2020
}
}
My Gulp task looks like:
const eslint = require('gulp-eslint');
return gulp.src(['src/**/*.js'])
.pipe(eslint({ configFile: '.eslintrc.json' }))
.pipe(eslint.formatEach('compact', process.stderr))
.pipe(eslint.failAfterError());
I'm using the following packages:
"devDependencies": {
"eslint": "^8.2.0",
"gulp": "4.0.2",
"gulp-eslint": "^6.0.0",
}
Am I missing something, or is there a viable workaround?
The ecmaVersion parser option can't be used like that, see https://eslint.org/docs/user-guide/configuring/language-options#specifying-environments.
Changing it from 2020 to 11 or 12 or "latest" should work.
The Problem
I am attempting to run a Cucumber test using a Karma test runner in my Npm project using the karma-cucumber-js adapter. However, when running my test, I find the following error in my console:
START:
PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
ReferenceError: Can't find variable: __adapter__
at features/Test.steps.js:80
For more context, I am in the middle of a platform migration. We had a complex system in place previously that essentially used karma 1.7.x, cucumber 1.2.x, karma-cucumber-js 0.3.3, and Node 0.8.x. I am trying to set up a simple npm project with the same exact versions (except now I'm using Node 8.x as I am not allowed to use 0.8.x). We have a suite of thousands of old tests that we'd like to minimize modifications for, and it would be easiest to get the karma-cucumber-js plugin working due to how tightly coupled our test system is with both karma and cucumber.
Things I've Tried
I played around with the dependent karma-cucumber-js source code a bit by tweaking the code in my package's node_modules directory as an attempt to better understand the issue at hand. I was able to bypass the issue above by modifying these lines in adapter.js to globalize the adapter variable in the window as follows:
var __adapter__;
(function (win) {
var adapter = new karma.CucumberAdapter(__karma__);
__adapter__ = adapter;
__karma__.start = adapter.getStart();
win.__adapter__ = __adapter__;
})(window);
Which lead to the next error:
PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
ReferenceError: Can't find variable: Cucumber
at node_modules/karma-cucumber-js/src/adapter.js:242
Which I worked around by adding the following line of code to the top of that very file after 'use strict':
var Cucumber = require('../../cucumber/release/cucumber.js');
Which lead to this issue:
PhantomJS 2.1.1 (Mac OS X 0.0.0) ERROR
ReferenceError: Can't find variable: CucumberHTML
at node_modules/karma-cucumber-js/src/adapter.js:177
And the workaround was to manually require that file adapter.js as well:
var CucumberHTML = require('../../cucumber-html/src/main/resources/cucumber/formatter/formatter.js');
With this "hack" in place, the cucumber tests are now able to run successfully:
START:
PhantomJS 2.1.1 (Mac OS X 0.0.0) LOG: 'Found features: /base/features/Test.feature'
PhantomJS 2.1.1 (Mac OS X 0.0.0) LOG: 'Tags: '
Is it Friday yet?
Friday is Friday
✔ Given today is Friday
✔ When I ask whether it's Friday yet
✔ Then I should be told "TGIF"
Finished in 0.002 secs / 0.002 secs # 19:36:12 GMT-0700 (PDT)
SUMMARY:
✔ 3 tests completed
The underlying issue seems to be that [these Typescript variables][4] declared at the end of the karma-cucumber-js packages are, for some reason, not available to use. Which I believe is due to a webpack wiring issue.
Some other things I've tried:
Using other karma-cucumber adapters (there are 2 others), no luck.
Playing around with karma/phantomjs/cucumber version combinations
Using Firefox and Chrome in replacement of PhantomJS
My questions
I need help understanding why this webpack setup is not working. How can I fix this?
Are there any steps I'm missing or alternative approaches I should be taking to debug and resolve the issue at hand?
I've included some of the project code below, thank you for your time!
Project Code
package.json:
{
"name": "npm-pretty-much-local-example",
"version": "1.0.0",
"scripts": {
"wp": "webpack",
"wpd": "webpack --debug",
"test": "./node_modules/karma/bin/karma start"
},
"devDependencies": {
"#babel/core": "^7.7.2",
"#babel/preset-env": "^7.7.1",
"#babel/preset-react": "^7.7.0",
"babel-loader": "^8.0.0",
"#babel/preset-typescript": "^7.7.x",
"babel-generator": "^6.26.1",
"babel-core": "^6.26.3",
"cucumber": "^1.2.x",
"jquery": "^3.4.1",
"karma": "^1.7.x",
"karma-cucumber-js": "^0.3.3",
"karma-mocha-reporter": "^2.0.5",
"karma-phantomjs-launcher": "^1.0.x",
"karma-webpack": "^1.7.x",
"socket.io": "^1.4.x",
"webpack": "^3.7.1",
"setimmediate": "^1.0.5",
"karma-typescript-preprocessor": "^0.4.0"
},
"dependencies": {}
}
webpack.config.js:
const path = require('path');
module.exports = {
context: path.resolve('.'),
entry: './src/index.js',
output: {
filename: "./dist/output.js"
},
resolve: {
extensions: ['.js'],
modules: [
path.resolve('./src'),
path.resolve('./node_modules')
]
}
};
Test.feature:
#Test
Feature: Is it Friday yet?
Everybody wants to know when it's Friday
Scenario: Friday is Friday
Given today is Friday
When I ask whether it's Friday yet
Then I should be told "TGIF"
Test.steps.js:
function isItFriday(today) {
if (today === "Friday") {
return "TGIF";
} else {
return "Nope";
}
}
// TODO: why is this not defined, and how was __adapter__ defined in the first place?
__adapter__.addStepDefinitions(function (scenario) {
scenario.Given('today is {string}', function (givenDay) {
this.today = givenDay;
});
scenario.When('I ask whether it\'s Friday yet', function () {
this.actualAnswer = isItFriday(this.today);
});
scenario.Then('I should be told {string}', function (expectedAnswer) {
return this.actualAnswer === expectedAnswer;
});
});
src/index.js
var dummyVar = "";
I want to use babel-plugin-transform-jsx, but no other transforms on some JSX files with some Javascript currently considered at stage 3 i.e. candidates.
Transpilation fails with a Syntax Error if those JSX files contain:
rest spread operators {...x, ...y}
async generators async function * () {}
The objective is to improve debug-ability of code in a modern browser, since the Babel transpilation of async generators in particular seems to break the dev tools for Chrome, Firefox i.e. breakpoints stop working, references to this fail, debugger calls are skipped, and numerous other observed problems.
There seems to be no alternative to using Babel to generate JSX in the above form — which works fine; an ideal solution would be to just have Babel ignore the async generators and rest-spread operators (and any other code it'd otherwise throw a syntax error on).
EDIT
Using the plugin suggested by Victor appears to be the correct solution but running babel-plugin-syntax-async-generators on this:
class SearchCriteria {
async * results (authManager) { }
}
Causes the error:
Unexpected token, expected "(" (2:10)
1 | class SearchCriteria {
> 2 | *async results(authManager) {
| ^
Reproducible here, when you add the syntax-async-generators plugin.
Babel has transform plugins and syntax plugins. Transform plugins apply transformations to your code. Syntax plugins allow Babel to parse specific types of JavaScript syntax, not transform it. Transform plugins will enable the corresponding syntax plugin so you don't have to specify both.
So in your case what you need is babel-plugin-transform-jsx transform plugin and two syntax plugins: babel-plugin-syntax-async-generators, babel-plugin-syntax-object-rest-spread.
The corresponding .babelrc will be:
{
plugins: ["babel-plugin-transform-jsx", "babel-plugin-syntax-async-generators", "babel-plugin-syntax-object-rest-spread"]
}
Minimal package.json:
{
"name": "babel-jsx",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"scripts": {
"build": "babel index.js"
},
"devDependencies": {
"babel-cli": "^6.26.0",
"babel-plugin-syntax-async-generators": "^6.13.0",
"babel-plugin-syntax-object-rest-spread": "^6.13.0",
"babel-plugin-transform-jsx": "^2.0.0",
"react": "^16.4.1"
}
}
And if you have JavaScript code like this in index.js:
import React from 'react'
const MyComp = <div>Hello</div>;
async function* myfunc() {
const array = [1, 2, 3];
console.log(...array);
}
And run commands:
yarn
yarn build
Then the output will be:
$ babel index.js
import React from 'react';
const MyComp = {
elementName: 'div',
attributes: {},
children: ['Hello']
};
async function* myfunc() {
const array = [1, 2, 3];
console.log(...array);
}
I used the following example to test tail call recursion with Babel and the es2016 preset:
'use strict';
try {
function r(n) {
if (n%5000===0)
console.log(`reached a depth of ${n}`);
r(n+1);
}
r(0);
} catch (e) {
if (!(e instanceof RangeError))
throw e;
else
console.log('stack blown');
}
My package.json file is:
{
"name": "tail-call-optimization",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"build": "babel es6 --out-dir es5 --source-maps",
"watch": "babel es6 --out-dir es5 --source-maps --watch",
"start": "node es5/app.js"
},
"author": "",
"license": "ISC",
"devDependencies": {
"babel-cli": "^6.6.5",
"babel-core": "^6.7.4",
"babel-loader": "^6.2.4",
"babel-polyfill": "^6.7.4",
"babel-preset-es2016": "^6.0.10",
"babel-runtime": "^6.6.1"
},
"dependencies": {
"babel-polyfill": "^6.7.4",
"source-map-support": "^0.4.0"
}
}
... and the .babelrc is simply:
{
"presets": ["es2016"]
}
Running the above with:
npm run build && npm run start
... results in the following console output:
reached a depth of 0
reached a depth of 5000
reached a depth of 10000
reached a depth of 15000
stack blown
Indeed, looking at the transpiled file in the es5 directory, there's nothing to suggest that TCO has been implemented.
Am I missing something?
My node version is 4.3.2.
Looking at: https://babeljs.io/docs/learn-es2015/ one reads:
Temporarily Removed in Babel 6
Only explicit self referencing tail recursion was supported due to the complexity and performance impact of supporting tail calls globally. Removed due to other bugs and will be re-implemented.
So I guess it's not presently implemented.
None of the "official" Babel 6 plugins / presets currently implements TCO. babel-preset-es2016 is not an "official" preset. Unless TCO relies on parser support in Babylon (off the top of my head I wouldn't think so, but I'm not sure) then I suppose a userland plugin / preset could implement it, and perhaps does (but not that I know of). Here is the issue tracking eventual "official" re-implementation: T2614. If someone wants to PR that link into the Learn ES2015 docs #Marcus mentioned ping me here and I'll merge it.
I am developing a web project using ES 6 in combination with the Babel transpiler and System.js as module loader. In all browsers from IE 9 up, that works well, unfortunately I have to support IE 8 though.
In the unbundled file, IE 8 can't even load my main.js file. It fails with the following console message without indicating any line of code.
Potentially unhandled rejection [4] SyntaxError: Error loading "main" at http://localhost:8080/assets/js/main.js
Error loading "npm:babel-core#5.8.22" at http://localhost:8080/assets/libs/npm/babel-core#5.8.22.js
expected identifier
I am using jspm as a dependency manager. The bundled sfx file built with jspm bundle-sfx yields a different error:
cannot read property "call" of undefined
Here, at least a line is given, it points to the following function of the system.js core:
function forEachGlobal(callback) {
iterateGlobals(function(globalName) {
if (indexOf.call(ignoredGlobalProps, globalName) != -1)
return;
try {
var value = loader.global[globalName];
}
catch(e) {
ignoredGlobalProps.push(globalName);
}
callback(globalName, value);
});
}
indexOf is indeed undefined here when logging it to the console, although it shouldn't, because it is defined earlier in the file.
The only relevant additional info I can give you is my package.json:
{
// some metadata
"devDependencies": {
"jspm": "^0.15.7",
"jspm-bower-endpoint": "^0.3.2",
// some more grunt tasks
},
"jspm": {
"directories": {
"baseURL": "source",
"packages": "source/assets/libs"
},
"dependencies": {
// frontend libraries
},
"devDependencies": {
"babel": "npm:babel-core#^5.1.13",
"babel-runtime": "npm:babel-runtime#^5.1.13",
"core-js": "npm:core-js#^0.9.4"
},
}
}
Officially, both system.js and babel support IE 8. I couldn't find everyone else with this issue, so I hope someone can help me solving that issue.
Thanks in advance!