Angular 2 AoT Rollup fails with 'require is not defined' - javascript

I have and Angular2 application that I'm compiling to run with AoT.
I've solved the issues I had with the actual compilation and I've also been able to run Rollup from start to end without errors (although there's a lot of warning, which I think are to be expected).
However, when running the application, the browser always states that require is not defined on my app.bundle.js.
What am I doing wrong?
Here's my functional non-AoT sample code/configs:
https://plnkr.co/edit/oCAaeXUKWGyd34YKgho9?p=info
And here is my functional AoT sample configs that throw the require error:
https://plnkr.co/edit/Y1C5HaQS3ddCBrbRaaoM?p=info
Does anyone find any errors here, especially when comparing the non-AoT system.js configs and the AoT rollup configs?
Why am I hitting this error?
I understand that the browser is incapable of working with require but shouldn't rollup attend to that?
Best Regards

So, eventually I was able to solve the issue.
There was a rogue const _ = require("lodash") in the code.
Once I removed it, it all went along without issues.
Two things worth mentioning:
Due to memory limitations on node.js (1.7GB of RAM), the ngc command is run with node --max-old-space-size=8192 ./node_modules/.bin/ngc -p tsconfig-aot.json
Again, for the same reason, rollup is run with node --max-old-space-size=8192 ./node_modules/.bin/rollup -c rollup-config.js
You might be able to get away with --max-old-memory=4096, depending on the size of your project and memory on your computer.
As for my rollup-config.js, though I'm not sure if everything here is really necessary, here's what worked for me:
import builtins from 'rollup-plugin-node-builtins';
import nodeResolve from 'rollup-plugin-node-resolve';
import commonjs from 'rollup-plugin-commonjs';
import uglify from 'rollup-plugin-uglify';
export default {
entry: 'app/app.aot.js',
dest: 'www/bundle.js', // output a single application bundle
sourceMap: false,
format: 'iife',
plugins: [
nodeResolve({
jsnext: true,
module: true,
browser: true
}),
commonjs({
// non-CommonJS modules will be ignored, but you can also
// specifically include/exclude files
include: [
'node_modules/**',
'node_modules/primeng/**',
'node_modules/moment/**',
'node_modules/rxjs/**',
'node_modules/lodash/**'
], // Default: undefined
exclude: ['node_modules/ws/**'], // Default: undefined
// search for files other than .js files (must already
// be transpiled by a previous plugin!)
extensions: ['.js'], // Default: [ '.js' ]
// if true then uses of `global` won't be dealt with by this plugin
ignoreGlobal: false, // Default: false
namedExports: {
// left-hand side can be an absolute path, a path
// relative to the current directory, or the name
// of a module in node_modules
'node_modules/primeng/primeng.js': [
'PanelModule',
'InputSwitchModule',
'InputMaskModule',
'ProgressBarModule',
'DropdownModule',
'CalendarModule',
'InputTextModule',
'DataTableModule',
'DataListModule',
'ButtonModule',
'DialogModule',
'AccordionModule',
'RadioButtonModule',
'ToggleButtonModule',
'CheckboxModule',
'SplitButtonModule',
'ToolbarModule',
'SelectButtonModule',
'OverlayPanelModule',
'TieredMenuModule',
'GrowlModule',
'ChartModule',
'Checkbox',
'Dropdown',
'Calendar',
'DataGridModule',
'DataTable',
'MultiSelectModule',
'TooltipModule',
'FileUploadModule',
'TabViewModule',
'AutoCompleteModule'
],
'node_modules/ng2-uploader/index.js': ['Ng2Uploader']
},
// if false then skip sourceMap generation for CommonJS modules
sourceMap: false, // Default: true
}),
builtins(),
uglify()
]
}
rollup still complains about default imports on some packages, which can be probably solved using the named exports (if you really want to) but even with those warnings everything seems to be running.
As for my "final" tsconfig.json:
{
"compilerOptions": {
"lib": ["es2015", "dom"],
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"declaration": false,
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": true,
"noImplicitAny": false,
"watch": false,
"skipLibCheck": true,
"allowSyntheticDefaultImports": true,
"suppressImplicitAnyIndexErrors": true,
"baseUrl": ".",
"typeRoots": [
"./node_modules/#types",
"./node_modules"
],
"types": [
"node",
"lodash",
"jasmine",
"bluebird",
"socket.io-client"
]
},
"compileOnSave": false,
"buildOnSave": false,
"files": [
"app/app.module.ts",
"app/app.aot.ts"
],
// "exclude": [
// "node_modules"
// ],
"angularCompilerOptions": {
"genDir": "compiled",
"skipMetadataEmit": true
}
}
Finally, these two links were also helpful in understanding what's going on behind the scenes:
https://code.lengstorf.com/learn-rollup-js/
https://github.com/rollup/rollup/issues/737
Hope this helps someone.

My solution for this problem was the following:
I tracked, what the system wants to require: the modules fs, events, and timer. All of them were referenced in the zone.js.
I've found some zone.js imports hacked into my code in my earlier tries to make in smaller as 5M.
After I removed them, the problem disappeared.
For the googlers of the future with a similar problem:
The cause of the problem is that your npm module uses require() internally. You have to update it, or to recompile it, but this time as ES6 package (which doesn't use require(), it uses import). If a require is really deeply hardcoded into it (for example, it is in .js and uses require), then you have to modify its source.
Additional extension:
It seems, rollup can't correctly handle non-ordinary imports like import 'Zone.js'; and similar! It can handle only import { something } from 'Anything';-like imports!
The root of all problems is that Angular requires zonejs imported on this way, furthermore any typescript decorator require the reflect-metadata package, imported also on this way.
But, it is not really a problem. Things looked so before, like
<script src="...zone.js"></script>
Or with an
import 'zone.js';
, shouldn't be exist in the source (or in the HTML) any more. Instead, you have to compile the sources without them, and then simply concatenate them to the beginning of your source. In my case, I use the following Grunt rule:
concat: {
dev: {
src: [ "node_modules/zone.js/dist/zone.js", "node_modules/reflect-metadata/Reflect.js", "target/MyApp-core.js" ],
dest: "target/MyApp.js"
}
},
It is part of the grunt-contrib-concat Grunt package.
The resulting target/MyApp.js can be further processed by any other tools (uglify, or the best is google closure compiler).

Related

Importing files in subdirectories when using Yarn Workspaces and Typescript

I'm having trouble porting a Yarn Workspaces monorepo to TypeScript. In the previous working version of the monorepo, moduleA could import something from a subdirectory of moduleB.
The new ported code attempts the same thing...
/* monorepo/packages/moduleA/src/helpers/misc.ts */
export const someHelper = () => console.log('hello world')
/* monorepo/packages/moduleB/src/index.ts */
import {someHelper} from 'moduleA/src/helpers/index.js'
This yields the error:
Error [ERR_MODULE_NOT_FOUND]: Cannot find module 'monorepo/node_modules/moduleA/src/helpers/misc.js' imported from monorepo/packages/moduleB/lib/index.js
My TsConfig for both packages:
{
"compilerOptions": {
"declaration": true,
"declarationMap": true,
"sourceMap": true,
"composite": true,
"outDir": "lib",
"rootDir": "src",
"allowJs": true,
"esModuleInterop": true,
"moduleResolution": "node",
"module": "es6"
},
"include": ["src"],
}
And added config for the moduleB package:
"references": [
{
"path": "../moduleA"
}
]
Note that I am building moduleB with tsc --build.
One way to solve the issue would be to simply export everything I need from moduleA as named exports in the entry point of the module. The reason I don't want to do this is because they may not be needed in the corresponding distribution which gets built from various parts of the monorepo. Importing and exporting them in moduleA would mean bundling code and sometimes running code that didn't need to be run, or shouldn't be run due to the environment.
Another thing to note is that if I rename monorepo/packages/moduleA/src/helpers/misc.ts with a .js extension instead of typescript; it works.
I can also seemingly change the error by adding this to the "compilerOptions" in moduleB:
"baseUrl": "src",
"paths": {
"moduleA/helpers/*": ["../moduleA/src/helpers/*"]
},
And changing the import statement to:
/* monorepo/packages/moduleB/src/index.ts */
import {someHelper} from 'moduleA/helpers/index.js'
But this gives me a type definition error:
src/index.ts:2:28 - error TS2307: Cannot find module 'moduleA/helpers/misc.js' or its corresponding type declarations.
Perhaps the above use of the paths in the TsConfig is not the way to go.
To summarise, the ultimate goal here is to allow importing of any of the files from moduleA's subdirectories to use in moduleB, without having to publish a separate module for that purpose.

How do I import libraries in TypeScript in general?

I'm new to TypeScript and just digging into that rabbit hole. I think, I understand the concept so far but what I don't understand is how I finally make use of libraries (in my case D3 for manipulating SVG DOM). In vanilla Javascript I simply did it the old-fashion way by including the library and my main.js script one after another but as my project grows I want to use the advantages of TypeScript with a modular aproach.
Current issue is this browser error (Chrome):
Uncaught TypeError: Failed to resolve module specifier "d3". Relative references must start with either "/", "./", or "../"
Well, I know it points to my import statement but I cannot figure out how to solve it.
tsconfig.json
{
"compilerOptions": {
"target": "ES5",'ESNEXT'. */
"module": "ESNext",
"declaration": true,
"outDir": "./dist/fgn/",
"rootDir": "./src/fgn/",
"strict": true,
"allowSyntheticDefaultImports": true,
"esModuleInterop": true,
"skipLibCheck": true, // required to avoid checking d3 type definitions
"forceConsistentCasingInFileNames": true
}
// ,"files": [],
// "include": [],
// "exclude": []
}
index.html (excerpt)
<head>
<script src="../dist/fgn/fgn.js" type="module"></script>
</head>
<body>
<button id="testButton" onClick="unreachableFoo();">Test Script</button>
</body>
./src/fgn/fgn.ts
/* Assuming I want to use the whole library. That's how it is documented by Mike Bostock.
However, this import statement throws an error in the browser (tested in Chrome, Firefox)
when being transpiled to javascript */
import * as d3 from "d3"; // points to '[root]/node_modules/#types/d3' (index.d.ts)
console.log('script is running...');
// This is the desired functionality I want to gain: having access to d3 from global namespace (globalThis ?)
d3.select('#testButton').on('click', () => {alert('button clicked!');});
// Also this is not working. The browser complains the function is not defined.
function unreachableFoo() { console.log('foo'); }
I have tried multiple tsconfig setups and changed the import to point to the "[root]/node_modules/d3/" (index.js) while enabling "allowJs" in tsconfig but this led to further issues as tsc somehow included the node_modules/#types/ path causing a mess-up and errors with the declaration files there.
Another attempt was to using webpack, setting up the package.json and building up the dependencies from there. Maybe I am on the right track with that but the browser error was still there.
What am I missing?
Welcome! The D3 documentation is going to be a little tougher to follow because it is using the import statement for JS and not TS. Try switching your import statement to
import { *desired methods* } from 'd3'; // there is no default d3 export
You will want to also undo your changes to your tsconfig.
Got it,
I simply missed the step of browserify. Instead of webpack, I am now using Rollup. For anyone encountering similar difficulties to getting started on that topic, here are some good explanations (take time to follow each):
How to Setup a TypeScript project using Rollup.js -- Rollup installation and required tsconfig.json setup; not all of the proposed installs are required, though.
Missing global variable names #162 -- rollup.config.js setup
#rollup/plugin-node-resolve -- usage of this plugin, in addition
Official rollup.js documentation -- RTFM
However, the Rollup specific fields in the rollup.config.js have changed over time, e.g. the 'entry' attribute was renamed to 'input'. Here's my current config following the above tutorials:
/* buil-in */
import resolve from '#rollup/plugin-node-resolve';
/* Install both plugins:
npm i #rollup/plugin-babel #rollup/plugin-commonjs --save-dev
*/
import commonjs from '#rollup/plugin-commonjs';
import babel from '#rollup/plugin-babel';
export default [
{
input: 'src/scripts/main.js', // change this accordingly
plugins: [
resolve({
jsnext: true,
main: true,
browser:true,
}),
commonjs(),
babel({
exclude: 'node_modules/**',
})
],
output: {
name: 'main',
file: 'public/scripts/main.js', // change this accordingly
/* format: 'iife' wraps everything into a self-executing function
(function () { ... }());
*/
format: 'iife' // or 'cjs'
}
}
];
This does not yet minify the code, though. It's just a development setup but you can add a production one as well.
package.json build script
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1",
"build": "tsc -b && rollup -c rollup.config.js"
}

getting nyc/istanbul coverage report to work with typescript

I'm struggling to get proper coverage with nyc/istanbul for my typescript/mocha/gulp project. I've tried a number of approaches, some of them seem to be unable to use source maps and other fails due to ts-node/tsc errors. My current setup is:
nyc relevant config in package.json
"scripts": {
"test:coverage": "nyc npm run test:unit",
"test:unit": "gulp mocha"
}
"nyc": {
"check-coverage": true,
"all": true,
"extension": [
".js",
".jsx",
".ts",
".tsx"
],
"include": [
"src/**/!(*.test.*).[tj]s?(x)"
],
"reporter": [
"html",
"lcov",
"text",
"text-summary"
],
"report-dir": "docs/reports/coverage"
}
gulpfile.js mocha relevant part
const SRC_DIR = path.join(__dirname, 'src');
const SRC_FILES = path.join(SRC_DIR, '**', '*.[jt]s?(x)');
const TEST_FILES = path.join(SRC_DIR, '**', '*.test.[jt]s?(x)');
const MOCHA_CONFIG = {
src: [
TEST_FILES
],
watchSrc: [
SRC_FILES,
TEST_FILES
],
mocha: {
// compilers: [
// 'ts:ts-node/register',
// 'tsx:ts-node/register'
// ],
require: [
'./tests/setup.js',
'ignore-styles',
'source-map-support/register'
]
}
};
gulp.task('mocha', mocha(MOCHA_CONFIG));
tsconfig.json
{
"compilerOptions": {
"baseUrl": "./",
"rootDir": "./src",
"outDir": "./build",
"allowJs": true,
"module": "commonjs",
"target": "es5",
"lib": ["es5", "es6", "dom"],
"sourceMap": true,
"inlineSourceMap": false,
"inlineSources": false,
"experimentalDecorators": true,
"noUnusedParameters": true,
"noUnusedLocals": true,
"jsx": "react",
"moduleResolution": "node"
},
"exclude": [
"docs",
"tests",
"**/*.test.js",
"**/*.test.jsx",
"**/*.test.ts",
"**/*.test.tsx",
"tools",
"gulpfile.js",
"node_modules",
"build",
"typings/main",
"typings/main.d.ts"
],
"awesomeTypescriptLoaderOptions": {
"useCache": true,
"useBabel": true
}
}
With the above setup coverage produces results for all the files but they are incorrect for TS files most probably due to source maps not being used (i.e. report shows no coverage for lines which are comments and the numbers seems to be wrong as well).
With a number of variant approaches tried with no success one of the most commonly suggested is to add "require": ["ts-node/register"] to nyc configuration yet then I'm getting errors complaining about i.e. gulpfile.js, docs/reports/coverage/lcov-report/prettify.js and number of other JS files to be not under 'rootDir' which is correct yet it is not clear why ts-node tries to process all the files out of src even if they are excluded in tsconfig.json (still the configuration gets really complex).
I'll appreciate any suggestion which way to go to get proper coverage report for TS files.
Recently I found a satisfiable solution by using "target": "es6" instead of es5 in tsconfig.json's compilerOptions. While changing target directly in tsconfig.json may not be an option as it affects build, the other tip is to use TS_NODE_COMPILER_OPTIONS='{"target":"es6"} which can be added directly in package.json scripts as i.e. :
"test:coverage": "TS_NODE_COMPILER_OPTIONS='{\"target\":\"es6\"}' nyc npm run test:unit",
where test:unit is whatever way being used to run actual tests (in my case just gulp mocha.
NOTE: I've also updated nyc to latest 11.1.0 and ts-node to 3.3.0 as suggested on https://github.com/istanbuljs/nyc/issues/618 thread
I'm not sure this is the same problem but I'll put this here in case it helps future developers...
I wasn't getting any coverage data until I added exclude-after-remap=false to the nyc section of my package.json.
This is listed in the documentation but not in a very prominent way (IMO).
Since a lot of changes broke old working setups I created a verbose example project covering typescript + mocha + nyc supporting proper coverage also for non called files (this is often not included in examples) as well as some unit test examples and quality checks using latest versions.
I had several issues whilst going to mocha 8+ nyc 15+. Maybe it also helps someone else stumbling across it.
https://github.com/Flowkap/typescript-node-template
If you're only interested in coverage check the .ncyrc.yml and mocharc.yml as well as the call config in package.json. VsCode launch configs also included:
.nycrc.yml
extends: "#istanbuljs/nyc-config-typescript"
reporter:
- html
- lcovonly
- clover
# those 2 are for commandline outputs
- text
- text-summary
report-dir: coverage
.mocharc.yml
require:
- ts-node/register
- source-map-support/register
recursive: true
color: true
exit: true
extension:
- ts
- test.ts
test job in package.json
"test": "npm run lint && nyc mocha src test",

"OuterSubscriber is not defined" when bundeling with rollup.js

After updating to Angular2 RC7 I get the folloing JavaScript error when running in the browser:
OuterSubscriber is not defined
This only happens when I create a bundle using rollup.js. If I run the application with the JavaScript not bundled it works fine.
The error must somehow be related with rxjs since OuterSubscriber is part of it. I checked the bundle and could not find OuterSubscriber there. I suppose rollup.js thinks that it is not necessary and therefore does not include it.
Environment:
angular, v2.0.0-rc.7
rxjs, v5.0.0-beta.12
systemjs, v0.19.27
gulp-rollup, v2.4.0
rollup-stream, v1.13.0
In the system.js config I map the umd modules (e.g. core.umd.js) for angular. For rxjs I use a classical mapping as in this example.
Does anyone have an idea what I'm doing wrong here?
Rollup 0.34.0 works with 2.0.0-rc.7 and verified Rollup 0.34.0 works with 2.0.0 also but I was able to replicate the issue with 0.35.0
There seem to be two problems with rollup currently.
rollup doesn't normalize slashes in paths ('/' vs '\\') and that issue is still open. To fix it rollup.config.js needs the following addition:
resolveId(id, from){
if (id.startsWith('rxjs/') || id.startsWith('rxjs\\')){
let result = `${__dirname}/node_modules/rxjs-es/${id.replace('rxjs/', '')}.js`;
return result.replace(/\//g, "\\"); // Add this
}
}
It breaks ES2015 code, but ES5 works fine, so it needs compiling rxjs-es to ES5 with ES2015 modules and making rollup resolver use it instead. Here is a separate tsconfig.rx.json:
{
"compilerOptions": {
"target": "ES5",
"module": "ES2015",
"moduleResolution": "node",
"sourceMap": true,
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"removeComments": true,
"noImplicitAny": false,
"skipLibCheck": true,
"outDir": "temp/rxjs",
"allowJs": true
},
"include": [
"node_modules/rxjs-es/**/*.js"
],
"exclude": [
"wwwroot",
"node_modules/rxjs-es/rx.js"
]
}
and rollup.config.js:
resolveId(id, from){
if (id.startsWith('rxjs/') || id.startsWith('rxjs\\')){
let result = `${__dirname}/temp/rxjs/${id.replace('rxjs/', '')}.js`;
//let result = `${__dirname}/node_modules/rxjs-es/${id.replace('rxjs/', '')}.js`;
return result.replace(/\//g, "\\");
}
}

Webstorm 11 check typescript files without compiling. Using webpack for compilation

i'm trying to use webstorm with webpack and typescript and i have stuck with errors checking.
I want to compile ts files with webpack, so i need to avoid compiling source files via Webstorm, but it seems like Webstorm perform error checking only during compilation process.
Corresponding to webstorm docs "Resolve objects using tsconfig.json" should activate Errors checking without compilation, but is doesnt.
example code, which Webstorm doesnt highlight
{ let z = 4;}
console.log(z);
my tsconfig file:
{
"compilerOptions": {
"module": "commonjs",
"out": "build/tsc.js",
"target": "es5",
"sourceMap": true,
"jsx": "react"
},
"exclude": [
"node_modules"
]
}
In same time Visual studio code display errors fine.
Do i have any errors in my configs ?
Is it possible to highlight errors correct with Webstorm or other JetBrains IDE?
Typescript version 1.7, Webstorm 11.
Original Answer (outdated - see UPDATE below):
As Valery correctly pointed out you can set the "noEmit" property to true to prevent the compiler creating any output files.
However, if your Webpack setup uses the same tsconfig.json file it will also prevent webpack from creating the output files. You'll only notice this the next time webpack is restarted.
In my webpack setup I'm using the "ts-loader" Typescript loader.
As mentioned on the ts-loader github page you can override compiler options.
So this is my setup:
tsconfig.json (used by IDE and Webpack)
"compilerOptions": {
"noEmit": true, // do not emit js and map files
...
webpack.config.json (used by Webpack)
{
test: /\.ts$/,
loader: 'ts-loader',
query: {
'compilerOptions': {
"noEmit": false // make sure js files do get emitted
}
},
...
}
Et voila: IDE compiler checks without js and js.map files polluting your source code folder!
UPDATE: My answer was a valid workaround in January but today the better solution is to use the Typescript service inside Webstorm/IDEA as suggested by anstarovoyt.
Also don't forget to UNcheck "Enable TypeScript Compiler" as shown here:
WebStorm 2016.1 (and other IJ 2016.1 IDEs) supports "compileOnSave" option. Example:
{
"compileOnSave": false,
"compilerOptions": {
"module": "commonjs"
}
}
Update: WebStorm 2016.2 has the new 'TypeScript service' integration.
You don't need the 'TypeScript complier' integration at all. Just check the option 'Use TypeScript service'. Moreover the new integration works much more faster.
Update 2: The integration is enabled by default in WebStorm 2016.3
I found the way to prevent compiler output vis tsconfig - noEmit option.
{
"compilerOptions": {
"module": "commonjs",
"noEmit": true,
"target": "es5",
"sourceMap": true,
"jsx": "react"
},
"exclude": [
"node_modules"
]
}
With this config i have no extra file and correct error highlight in webstorm.
In your Webpack configuration file add this in module.rules:
{
test: /\.tsx?$/,
use: [
{
loader: 'ts-loader',
options: {
compilerOptions: {
noEmit: false,
},
},
},
],
}
Obviously, if you already have a rule for test: /\.tsx?$/, you should combine it with the above.
And note that module.rules is an array of objects, not an object.

Categories