I am watching a video and the video says that you write
this code in .babelrc:
{
"presets": ["#babel/preset-env"]
}
to transform the es6 code I wrote to common js code that node js can execute. But the problem is that it is unable to load schema from that code above.
Related
You will find instructions to reproduce on your own device at the bottom.
I have a basic Angular project I created using Angular CLI, running on TypeScript 3.1.3, with nothing much added aside a class and a json file.
I created a class ResourcesService with the following command with Angular CLI:
ng generate service Resources
I'm basically using it to load json files, as a mean of localising (instead of using Angular unfinished builtin internationalisation features).
The following is my class, as well as the json file:
ResourcesBundle.json
{
"label.changeLanguage": "Change language",
"label.education": "Education",
"label.experience": "Experiences",
"label.skills": "Skills",
"label.summary": "Summary",
"label.language.english": "English",
"label.language.french": "French"
}
resources.service.ts
import * as resources from '../assets/resources/ResourcesBundle.json';
#Injectable({
providedIn: 'root'
})
export class ResourcesService {
constructor() {}
public getString(label: string): string {
let resource: string = resources[label];
return resource;
}
}
Of course, in order to be able to import the json file that way, I've set "resolveJsonModule": true in tsconfig.json.
The service by itself is working properly. I can inject it and call the getString method, and it's running without any error.
However, no matter what value I pass to the getString method, the returned value is always undefined. I've even tried to hard code the value for label = 'label.summary', but it's still returning undefined. The only time it's working properly is when I write the string directly between the brackets:
let resource: string;
label = 'label.summary';
resource = resources[label]; // resource == undefined
resource = resources['label.summary']; // resource == 'Summary'
Within the TS on VSCode, the content of resources is as following:
label.changeLanguage
label.education
label.experience
label.language.english
label.language.french
label.skills
label.summary
When using console.log(resources), the console was displaying something like this on Firefox:
Object {
label.changeLanguage: "Change language"
label.education: "Education"
label.experience: "Experience"
label.language.english: "English"
label.language.french: "French"
label.skills: "Skills"
label.summary: "Summary"
}
So the json is properly loaded, but it can only be used with hard coded string.
The only other solution I found was to give up the json file and initialise an indexed type directly in the code:
private resources: { [key: string]: string } = {
'label.changeLanguage': 'Change language',
'label.education': 'Education',
'label.experience': 'Experiences',
'label.skills': 'Skills',
'label.summary': 'Summary',
'label.language.english': 'English',
'label.language.french': 'French'
};
However, I don't think that's a good approach, at all...
In the case of the imported json file, why does it always return undefined when I use a variable? Or otherwise, why does it work only with a hard coded string between the brackets?
Edit:
You will find below a stackblitz link to a demo project:
https://stackblitz.com/edit/angular-h2aspf?file=tsconfig.json
If you run it on the browser, it will work properly (the console will properly display Change language).
However, if you download it and run it locally, you will notice that the console will display undefined instead.
To run it locally:
You must have npm and Angular CLI
Download and unzip the stackblitz demo in a folder
Run npm i in the project folder
Run ng serve --open
Open the console on your browser, it should be displaying undefined, instead of the expected value (Change language on stackblitz)
Edit:
According to a comment on the Angular CLI issue, a workaround is to set "esModuleInterop": true in tsconfig.json, and to change the import statement from:
import * as resources from '../assets/resources/ResourcesBundle.json';
To this:
import resources from '../assets/resources/ResourcesBundle.json';
Original answer:
After checking multiple times on different devices, I think this is a bug directly related to Angular (current version: 7.0.2).
To take the example I gave in the question again:
https://stackblitz.com/edit/angular-h2aspf?file=tsconfig.json
On the browser, this demo is outputting Change language on the console.
On locale device:
Download and unzip the stackblitz demo in a folder
Run npm i in the project folder
If you run with ng serve, you will notice undefined in the web browser console
Stop Angular, then run again with ng serve --prod. The web browser console is now properly outputting Change language
I've opened the following issues for Angular and Angular CLI projects on GitHub for this problem:
Angular: Issue #26785: Imported json file as indexed type always giving undefined when Angular is not running in production mode
Angular CLI: Issue #12781: Imported json file as indexed type always giving undefined, but not when running ng serve --prod
I'm happily using node 8.6 with the experimental ES6 modules option (--experimental-modules) turned on. This allows me to perfectly write plain ES2015 code for node without the need of babel.
The problem is when I try to create some tests with jest, it fails complaining about a syntax error: "Unexpected token import".
The .babelrc configuration is the following:
{
"env": {
"test": {
"presets": [
["env", {
"targets": {
"node": "8.6"
}
}]
]
}
}
}
My jest.config.js is as follows:
module.exports = {
testMatch: ['/tests/**/*.js', '**/?(*.)test.js'],
}
The error thrown:
/app/tests/integration/controller/data-provider/Credentials/CredentialsList.action.test.js:2
import { Credentials, AdWordsCredentials } from '../../../../../imports/models/data-provider/Credentials.mjs';
^^^^^^
SyntaxError: Unexpected token import
at ScriptTransformer._transformAndBuildScript (node_modules/jest-runtime/build/script_transformer.js:305:17)
at Generator.next (<anonymous>)
at Promise (<anonymous>)
Relevant packages:
babel-core#^6.26.0
jest#^21.2.1
babel-jest#^21.2.0
babel-preset-env#^1.6.0
Any help will be appreciated.
Thanks :)
UPDATE: I've tried calling jest without babel, with the following command, without any change: node --experimental-modules node_modules/.bin/jest
Jest has a custom implementation of require to help with mocking. Unfortunately, this makes jest incompatible with node --experimental-modules. Babel is probably the best way to use ES6 modules with jest. See https://github.com/facebook/jest/issues/4842
I was not used jest, and I am not sure if this will solve, but I hope this can help you.
Node still doesn't support all syntax. If you really are looking a faster way to start develop, using source code with all features of Ecmascript2017, you need a module like #kawix/core https://www.npmjs.com/package/#kawix/core
How the README.md says, allows you to use all features including "imports" and "async/await" and also supports typescript, and other good features all without a LOT OF DEPENDENCIES. You can use directly with cli:
> npm install -g #kawix/core
> kwcore /path/to/fullsyntaxtsupport.js
Or if you want inclute programatically, create a file example main.js to import the fully syntax file
var kawix= require("#kawix/core")
kawix.KModule.injectImport()
kawix.KModule.import("/path/to/fullsyntaxtsupport.js").catch(function(e){
console.error("Some error: ",e)
})
After installing the AoT plugin (https://www.npmjs.com/package/#ngtools/webpack) for webpack, dynamic requires no longer work:
// Example that used to work
public getJson<T>(fileName: String): T {
return require(`../../${fileName}_${this.lang}.json`);
}
With the standard ts-loader or awesome-typescript-loader etc, dynamic requires worked and webpack bundled the json files into the main app bundle. However, with the AoT/Webpack plugin the json files are not bundled at all. I don't even think the aot loader iterates over the json files anymore.
Any ideas how to get this to work again? Thanks.
Info:
https://github.com/angular/angular-cli/issues/3306
https://github.com/angular/angular-cli/pull/4153
Update:
Works somewhat with SystemJS -> System.import() but erratically
https://github.com/angular/angular-cli/issues/6629#issuecomment-336411537
Workaround is to use System.import() to build load and bundle the dynamic files then use the standard webpack mechanism to load the actual files:
public getLazyFiles<T>(somePath: string): T {
/* AoT Hack - causes the AoT to find and prepare the dynamically loaded files */
System.import(`../../${somePath}_${this.someSuffix}.json`);
/* ------- */
// This is then used by webpack to actually load the files
return require(`../../${somePath}_${this.someSuffix}.json`);
}
Why this workaround is needed is explained here: https://github.com/angular/angular-cli/issues/6629#issuecomment-336478854
Newbie. I'm setting up gulp-webpack to run in a Gulp task, and I'm starting simple by processing a test "less" file. I npm-installed gulp-webpack, webpack, and also the webpack loaders less-loader, css-loader, and style-loader. For some reason, my output css file isn't ... instead it's a js file containing what appears to be source code for a loader itself. Do I need to require the webpack loaders within the webpack config file?
My gulpfile.js:
var gulpwebpack = require('gulp-webpack');
gulp.task('go2',function(){
return gulp.src('src/homesMenuAngular/css/*.less')
.pipe(gulpwebpack( require('./webpack.config.js') ))
.pipe(gulp.dest('builds/homesMenuAngular/css'));
});
My webpack.config.js:
// no require statements
module.exports = {
module: {
loaders: [
{
test: /\.less$/,
loader: "style!css!less"
}
]
}
};
So, most experienced webpackers are probably rolling their eyes, because yes, webpack does output javascript that injects all the other resources into the page. I was thinking "let's use a webpack loader to convert less to css" and "let's use gulp" to automate the whole build process but I hadn't taken a deep enough dive into how webpack works specifically that it bundles everything into 1 or more js files.
I am currently learning Meteor and I found out something that intrigued me.
I can load HTML and CSS assets from a JS file using the import statement.
import '../imports/hello/myapp.html';
import '../imports/hello/myapp.css';
import * as myApp from '../imports/hello/myapp.js';
This was a surprise to me so I ran to google but could not find this behavior documented in the specification for ES6 import or in Meteor's Docs.
So my questions are:
Can I rely on this behavior to build my apps?
Will my app will break when Meteor gets around to fix it -- if it's a bug --?
Notes
I am using Meteor v1.3, not sure if this works also with previous versions.
You can download the app to see this behavior from Github
After going through the implementation of the built files for my app
I found out why this works.
HTML
Files are read from the file system and their contents added to the global Template object, e.g.,
== myapp.html ==
<body>
<h1>Welcome to Meteor!</h1>
{{> hello}}
</body>
results in the following JS code:
Template.body.addContent((function () {
var view = this;
return [
HTML.Raw("<h1>Welcome to Meteor!</h1>\n\n "),
Spacebars.include(view.lookupTemplate("hello"))
];
}));
Which is wrapped in a function with the name of the file as it's key:
"myapp.html": function (require, exports, module) {
Template.body.addContent((function () {
var view = this;
return [
HTML.Raw("<h1>Welcome to Meteor!</h1>\n\n "),
Spacebars.include(view.lookupTemplate("hello"))];
}));
Meteor.startup(Template.body.renderToDocument);
Template.__checkName("hello");
Template["hello"] = new Template("Template.hello", (
function () {
var view = this;
return [
HTML.Raw("<button>Click Me</button>\n "),
HTML.P("You've pressed the button ",
Blaze.View("lookup:counter",
function () {
return Spacebars.mustache(view.lookup("counter"));
}), " times.")
];
}));
},
So all of our HTML is now pure JS code which will be included by using require like any other module.
CSS
The files are also read from the file system and their contents are embedded also in JS functions, e.g.
== myapp.css ==
/* CSS declarations go here */
body {
background-color: lightblue;
}
Gets transformed into:
"myapp.css": ["meteor/modules", function (require, exports, module) {
module.exports = require("meteor/modules").addStyles("/* CSS declarations go here */\n\nbody {\n background-color: lightblue;\n}\n");
}]
So all of our CSS is also now a JS module that's again imported later on by using require.
Conclusion
All files are in one way or another converted to JS modules that follow similar rules for inclusion as AMD/CommonJS modules.
They will be included/bundled if another module refers to them. And since all of them are transformed to JS code
there's no magic behind the deceitful syntax:
import '../imports/hello/myapp.html';
import '../imports/hello/myapp.css';
They both are transpiled to their equivalent forms with require once the assets have been transformed to JS modules.
Whereas the approach of placing static assets in the imports directory is not mentioned in the official documentation,
this way of importing static assets works.
This seems to be at the core of how Meteor works so I'd bet this functionality is going to be there for a long while.
I don't know if to call this a feature maybe a more appropriate description is unexpected consequence but that would
only be true from the user's perspective, I assume the people who wrote the code understood this would happen and perhaps even
designed it purposely this way.
One of the features in Meteor 1.3 is lazy-loading where you place your files in the /imports folder and will not be evaluated eagerly.
Quote from Meteor Guide:
To fully use the module system and ensure that our code only runs when
we ask it to, we recommend that all of your application code should be
placed inside the imports/ directory. This means that the Meteor build
system will only bundle and include that file if it is referenced from
another file using an import.
So you can lazy load your css files by importing them from the /imports folder. I would say it's a feature.
ES6 export and import functionally are available in Meteor 1.3. You should not be importing HTML and CSS files if you are using Blaze, the current default templating enginge. The import/export functionality is there, but you may be using the wrong approach for building your views.