How can I get webpack to find angular modules? - javascript

I'm trying to setup a bare-bones application with Angular 1 + Typescript 2 and Webpack. The app works fine until I try to use an external module, ex: angular-ui-router.
It always complains that it can't find the dependency:
ERROR in ./src/app.ts Module not found: Error: Cannot resolve module 'angular-ui-router' in ./src/app.ts 3:26-54
Demo showing problem: https://github.com/jxc876/angular-ts
I suspect I'm not importing the routing dependency correctly, tried:
import uiRouter from 'angular-ui-router';
import * as uiRouter from 'angular-ui-router'
Tried with angular-route and also ui-router but neither works. Tried ts-loader and awesome-typescript-loader.
App
import * as angular from 'angular';
import uiRouter from 'angular-ui-router';
let myApp = angular.module('myApp', [uiRouter]);
myApp.config(function($stateProvider) {
let homeState = {
name: 'home',
url: '/home',
template: '<div>It works !!!</div>'
}
$stateProvider.state(homeState);
});
Config
package.json
{
"name": "ts-demo",
"scripts": {
"start": "webpack-dev-server --content-base ./src"
},
...
"devDependencies": {
"#types/angular": "^1.5.16",
"#types/angular-ui-router": "^1.1.34",
"awesome-typescript-loader": "^3.0.0-beta.3",
"typescript": "^2.0.9",
"webpack": "^1.13.3",
"webpack-dev-server": "^1.16.2"
},
"dependencies": {
"angular": "^1.5.8",
"angular-ui-router": "^0.3.1",
"enhanced-resolve": "^2.3.0"
}
}
webpack.config.js
module.exports = {
entry: './src/app',
output: {
filename: './dist/bundle.js'
},
resolve: {
extensions: ['.ts', '.tsx', '.js', '.jsx']
},
devtool: 'source-map',
module: {
loaders: [
{
test: /\.ts$/,
loader: 'awesome-typescript-loader'
}
]
}
};
tsconfig.json
{
"compilerOptions": {
"outDir": "./dist/",
"allowJs": true,
"target": "es5",
"module": "commonjs",
"moduleResolution": "node",
"strictNullChecks": true,
"listFiles": true
},
"include": [
"./src/**/*"
],
"exclude": [
"node_modules"
]
}

Finally figured this out.
First issue is that the typescript compiler removes import statements that are not used.
The compiler detects whether each module is used in the emitted JavaScript. If a module identifier is only ever used in type annotations and never as an expression then no require call is emitted for that module. This culling of unused references is a good performance optimization, and also allows for optional loading of those modules.
source: https://github.com/Microsoft/TypeScript/issues/4717
I assigned the imported value to a dummy array and it seems to fix this. Logging the value out to the console also works. (See my final note on why I couldn't simply pass it into the dependency array).
EDIT: Better solution is to use import "module"; syntax since that is always emitted based on the github above, ex: import 'angular-ui-router';
Second, my webpack file was missing an empty string in the resolve array:
resolve { extensions: ['', '.ts', '.js'] }
Without this it couldn't pull in the file for ui router.
A few things I noticed while working on this: webpack --display-error-details is super useful. For some reason it was looking for double .js.js extensions inside node_modules/angular-ui-router/release:
resolve file
/Users/mich2264/projects/angular-ts/node_modules/angular-ui-router/release/angular-ui-router.js.ts doesn't exist
/Users/mich2264/projects/angular-ts/node_modules/angular-ui-router/release/angular-ui-router.js.js doesn't exist
--traceResolution is equally useful for typescript.
EDIT: Appears to be a awesome-typescript-loader loader bug:
https://github.com/s-panferov/awesome-typescript-loader/pull/264
Finally, I'm not sure why but when I import the default value from angular-ui-router and I log it or set a breakpoint it shows up correctly as ui.router, but if I attempt to pass it into the dependency array it becomes undefined.
#types/angular-ui-router defines the following export inside their type file : export default "ui.router";

This one should do the job:
import * as angular from 'angular';
import * as uiRouter from 'angular-ui-router';
let myApp = angular.module('myApp', ['ui.router']);
Notice, that import is just importing router code and in application module you need to inject 'ui.router' string.

I suppose that since I'm using UI-Router for AngularJS (1.x), I had to use...
import '#uirouter/angularjs'
...instead of...
import 'angular-ui-router'

Related

How to bundle prop types in a reusable React component library

I'm trying to make a simple React components library for fun using Typescript and Storybook, and my first silly Button component seems to work. I have another CRA demo app where I am trying the library as a final user.
I am stuck with a thing though: I can't make the prop-types of my Button component to work in the demo application. It just doesn't print the warnings.
Some additional details:
Files and folder of the library are currently organized as follows:
src/
index.ts
controls/
index.ts
Button.tsx
babelconfig.js
tsconfig.ts
webpack.config.js
(+ other configuration files and folder for jest and storybook)
I have an src/index.ts file which is the entry point of my library and it just exports everything that comes from the controls folder.
// src/index.ts
export * from './controls'
In my src/controls folders there's an index.ts file which exports everything from the Button component.
// src/controls/index.ts
export * from './Button'
The Button component, at the moment, is as simple as:
import React from 'react'
import t, { InferProps } from 'prop-types'
const propTypes = {
text: t.string.isRequired
}
const Button = ({ text }: InferProps<typeof propTypes>) => (
<button>
{ text }
</button>
)
Button.propTypes = propTypes
export { Button }
Everything is bundled with webpack 4.41 with this configuration file:
// webpack.config.js
const path = require('path')
module.exports = {
mode: 'production',
entry: './src/index.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js',
library: 'sads-ui',
libraryTarget: 'umd'
},
externals: [
'react'
],
resolve: {
extensions: [ '.ts', '.tsx', '.js' ]
},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
}
]
}
}
My babel configuration is this one:
// babel.config.js
module.exports = {
presets: [
'#babel/preset-env',
'#babel/preset-react',
'#babel/preset-typescript'
],
env: {
test: {
plugins: [ 'require-context-hook' ]
}
}
}
I'm not sure if it may matter, but this is my typescript 3.7 configuration file:
// tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "es6",
"jsx": "react",
"declaration": true,
"outDir": "./dist",
"downlevelIteration": true,
"strict": true,
"noImplicitAny": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noImplicitReturns": true,
"noFallthroughCasesInSwitch": true,
"baseUrl": ".",
"typeRoots": [ "node_modules/#types" ],
"esModuleInterop": true
}
}
The main properties of my package.json file are these one:
// other properties
"main": "dist/index.js",
"files": [
"dist/*",
"!dist/**/*.stories.d.ts"
],
"scripts": {
"prebuild": "rimraf ./dist ./*.tgz",
"build": "webpack",
"prepack": "npm run build",
"test": "jest",
"storybook": "start-storybook"
},
// other properties
Expectations
In my demo app, created with npx create-react-app sadsui-demo, I installed the package in my package.json with a dependency like this:
// package.json
"sads-ui": "file:../sads-ui/sads-ui-0.1.0.tgz"
I use the Button in my App.js as follow:
import React from 'react'
import { Button } from 'sads-ui'
const App = () => (
<div className="App">
<Button />
</div>
)
export default App
I would expect to get a warning the tells me that the text prop of Button is required, but I get nothing. An empty button is correctly shown though.
My reasoning
I'm somehow in the dark but I have something in mind, not sure if it makes any sense, also because it doesn't seem to work either.
I think that building the library in webpack production mode may strip off prop-types from the library bundle thus I don't have them available when I use the components in final applications.
I tried to bundle in development mode, I see in the dist/index.js the prop-types code but they aren't working no matter what... :(
Your webpack is compiling (everything matching *.tsx) to javascript when building the library, and when you import it into your create-react-app it is just javascript (you should go into your /node_modules/ in your create-react-app and actually see for yourself what your library looks like)
In your library, you can export separate index.d.ts (or Button.d.ts or whatever) files for your components that are not compiled to JS that consumers can import
(Always good to look at other projects and see how they do it. Here is material ui's index.d.ts, you can probably be a lot cleaner than that by having a Button.d.ts that is imported into the index.d.ts and then exported as one big library typescript module

typescript and sweetalert2 Uncaught ReferenceError: exports is not defined

Started a brand new .net core 2.0 project to start learning and i have opted to make use of and learn typescript.
i have been following the guide located here: typescript guide
This compiles and works fine.
I then wanted to make use of sweetalert2 which i have used in the past and i followed these instructions sweetalert2
i created a simple helloWorld() in the ts file
import swal from 'sweetalert2'
function swalHelloWorld() {
swal('hello world!');
}
which compiles in a js file of my www folder too
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var sweetalert2_1 = require("sweetalert2");
function swalHelloWorld() {
sweetalert2_1.default('hello world!');
}
and included on the _layout page
Now when i run my project i get the following err
Uncaught ReferenceError: exports is not defined
at app.js:2 (anonymous) # app.js:2
line 2 is the following
Object.defineProperty(exports, "__esModule", { value: true });
i tried following the guide here to correct it but this didnt help
my tsconfig.json is
{
"compilerOptions": {
"noImplicitAny": true,
"noEmitOnError": true,
"sourceMap": true,
"target": "es5",
"module": "commonjs",
"moduleResolution": "node"
},
"files": [
"./scripts/app.ts"
],
"exclude": [
"node_modules",
"wwwroot"
],
"compileOnSave": true
}
i am unsure how to resolve this issue
webpack config
var path = require('path');
module.exports = {
entry: {
site: [
'./Scripts/app.ts']
},
output: {
filename: 'bundle.js',
path: path.resolve(__dirname, 'wwwroot/dist/')
},
module: {
rules: [
{
test: /\.tsx?$/,
loader: 'ts-loader',
exclude: /node_modules/,
},
]
},
resolve: {
extensions: [".tsx", ".ts", ".js"]
}
};
git repo: https://github.com/iamthebarnsley/TSExample
It looks like your HTML page is still referencing app.js. If you wanted to follow the guide you linked, the HTML page should instead reference the bundle.js file produced by Webpack.
Round 2
If you want to call swalHelloWorld from your HTML with <input id="swalalert" type="button" value="swal alert" onclick="swalHelloWorld();" />, then you need to define swalHelloWorld globally:
import swal from 'sweetalert2'
function swalHelloWorld() {
swal('hello from sweet alert');
}
(<any>window).swalHelloWorld = swalHelloWorld;
Without this, Webpack is being clever and realizing there is no way to call swalHelloWorld (since it is not exported from the module either) and omitting it from the output. When I make this change and also replace build/app.js with dist/bundle.js in the HTML as previously discussed, the alert is working for me.
Update, 2018-09-30
I learned about a cleaner solution: add the library option to the Webpack configuration as shown here with a name of your choice (for example, swalHelloWorld), and that will define a global variable named swalHelloWorld representing the entire entry-point module. Then if you export a function from the module:
import swal from 'sweetalert2'
export function swalHelloWorld() {
swal('hello from sweet alert');
}
the HTML will be able to call it as swalHelloWorld.swalHelloWorld(...) or similar.

Jest cannot locate #babel/code-frame when trying to use # alias

Our app imports files using import ES2015 style syntax, utilizing Webpack 4.6.0 native support for ES2015 modules. We also use an alias to shorten our relative file paths.
Webpack.conf.js
resolve: {
extensions: ['.js', '.json', '.less'],
alias: {
'#': resolve('public/js'),
'handlebars': 'handlebars/dist/handlebars.js',
},
modules: ['less', 'node_modules']
},
example.js
import widget from '#/widgets/widget';
file structure
- webpack.conf.js
- .babelrc
- test/
- public/
- - js/
- - - widgets/
- - - - widget.js
When I imported for example example.js, which has an alias'd import, Jest would throw an error, "cannot resolve module '#/widgets/widget'.
According to a remarkably specific article as well as the Jest documentation, the solution is to use Jest's ModuleNameMapper config property to set up matching alias'. I have attempted to do so:
package.json
"jest": {
"moduleNameMapper": {
"\\.(jpg|jpeg|png|gif|eot|otf|webp|svg|ttf|woff|woff2|mp4|webm|wav|mp3|m4a|aac|oga)$": "<rootDir>/__mocks__/fileMock.js",
"\\.(css|less)$": "<rootDir>/__mocks__/styleMock.js",
"#(.*)$": "<rootDir>/public/js/$1"
},
"verbose": true,
"transform": {
"^.+\\.js$": "babel-jest"
},
"globals": {
"NODE_ENV": "test"
},
"moduleFileExtensions": [
"js"
],
"moduleDirectories": [
"node_modules"
]
},
As well as properly configure babel:
.babelrc
{
"presets": [
[
"env",
{
"modules": false,
"test": {
"plugins": ["transform-es2015-modules-commonjs"]
}
}
],
"es2015",
"stage-2"
],
"plugins": [
"syntax-dynamic-import"
]
}
Now, when I run Jest (with the --no-cache flag just in case), I get this error:
test/test.test.js
● Test suite failed to run
Configuration error:
Could not locate module #babel/code-frame (mapped as /home/calebjay/Documents/ide/public/js/babel/code-frame)
Please check:
"moduleNameMapper": {
"/#(.*)$/": "/home/calebjay/Documents/ide/public/js/$1"
},
"resolver": undefined
I can't find #babel/code-frame anywhere outside of package-lock.json, and just for giggles I stripped all mentions of #{{anything}} from there and ran tests again, same result.
Is jest stepping over babel somehow? How can I get my tests to run with Jest using aliases?
EDIT: To try to narrow down what is calling #babel/code-frame, I tried deleting es2015 and stage-2 from .babelrc, to no effect. I tried deleting the transform property of the Jest config in package.json, to no effect. I tried deleting the env.test.plugins property from .babelrc, to no effect. Same error.
EDIT2: Thinking maybe some other package is requiring it, I checked package.json. It seems jest-message-util requires #babel/code-frame. I do see #babel/code-frame in my node_modules though... so perhaps the problem is that jester is saying "ok, all instances of #, turn into public/js" ?
"#(.*)$": "<rootDir>/public/js/$1"
will convert #babel/code-frame to
"<rootDir>/public/js/babel/code-frame"
which doesn't exist. You need to make your pattern more specific and do
"#/(.*)$": "<rootDir>/public/js/$1"
Note the additional / at the beginning. That way it will still match your #/widgets/widget, but it won't match other scoped packages.

Unable to load a react module as node module

I have a react component in the path
src/components/test
import React from 'react';
import ReactDom from 'react-dom';
class TestComp extends React.Component {}
export default TestComp;
I am exposing the component in index.js from path
src/index.js
import TestComp from './components/test';
export {
TestComp
};
I have added main in package.json as "main": "src/index.js"
I have published a npm package test-comp of above application and using same in another application.
main.js
import {TestComp} from 'test-comp';
I am using grunt-browserify in this application with following options set.
options: {
"transform": [
[
"babelify",
{
"presets": [
"es2015",
"react",
"stage-0"
]
}
]
],
browserifyOptions: {
debug: true,
extensions: ['.js', '.jsx'],
entries: ['main.js']
}
}
When I run grunt browserify getting following error.
>> import TestComp from './components/test';
>> ^
>> ParseError: 'import' and 'export' may appear only with 'sourceType: module'
Warning: Error running grunt-browserify. Use --force to continue.
It probably not understanding the path mentioned in node module or rejecting to understand the same which linting. I even have tried adding following in .eslintrc but no luck
{
"extends": "eslint:recommended",
"parserOptions": {
"ecmaVersion": 6,
"sourceType": "module"
},
"env": {
"browser": true,
"es6": true
},
"ecmaFeatures": {
"modules": true
}
}
I tried most of SO answers related to this error. But still stuck in same place.
EDIT
I am able to browserify first module directly with almost similar configuration. Getting this error when first module is loaded as node dependancy in other application as explained above.
So you wrote the module test-comp in ES6, using import and export, and the main entry of the package.json in test-comp refers to src/index.js.
The answer is that browserify transforms don't apply to every module you require. They only apply to the immediate project: not the project's dependencies.
If you want to require a module that uses ES6 syntax in browserify, you'll either need to
Add a prepublish script to test-comp that transpiles it to ES5, and change the main entry of test-comp to refer to that ES5 version, not the ES6 version
Add babelify as a dependency of test-comp and add babelify as a browserify transform in the package's 'browserify' entry, as documented in babelify.

Rollup.js: undefined objects in external dependencies

I recently started playing with rollupjs. After configuring everything as per available docs and bundling things up, I got many errors from my external libraries about undefined objects. This sort of errors: Cannot read property 'parse' of undefined coming from crypto-js.
It complains about this line in the code: var ciphertext = Base64.parse(openSSLStr). So Base64 is undefined. I have few errors like this from different external libraries bundled in.
I use a handful of external dependencies:
chart.js,
crypto-js,
mithril,
moment,
pluralize
All of them work perfectly with jspm. I decided to try rollup to speed things up as jspm is soooo slow at the moment. Now half of my external dependencies stopped working. I get "undefined things" and "...not a function" kind of errors coming from external libraries only.
What could possibly be the cause of it?
This is my rollup.config.js
import babel from 'rollup-plugin-babel';
import npm from 'rollup-plugin-npm';
import commonjs from 'rollup-plugin-commonjs';
import uglify from 'rollup-plugin-uglify';
export default {
entry: 'app/scripts/application/main.js',
format: 'cjs',
plugins: [
npm({
jsnext: true,
main: true,
}),
babel({
exclude: 'node_modules/**',
presets: [ 'es2015-rollup' ],
}),
commonjs(),
uglify(),
],
dest: 'static/js/application.js',
};
Let me know if any other details are needed.
Thanks.
EDIT
I've done a simple tests-reproduction bundling those libraries that generate errors in my application.
package.json
{
"name": "minion",
"private": true,
"babel": {
"presets": [
"es2015-rollup"
]
},
"dependencies": {
"chart.js": "^1.0.2",
"crypto-js": "^3.1.6",
"mithril": "^0.2.2-rc.1",
"moment": "^2.11.1",
"pluralize": "^1.2.1"
},
"devDependencies": {
"babel-preset-es2015-rollup": "^1.1.1",
"rollup-plugin-babel": "^2.3.9",
"rollup-plugin-commonjs": "^2.2.0",
"rollup-plugin-npm": "^1.3.0",
"rollup-plugin-uglify": "^0.1.0"
}
}
rollup.config.js
import babel from 'rollup-plugin-babel';
import npm from 'rollup-plugin-npm';
import commonjs from 'rollup-plugin-commonjs';
import uglify from 'rollup-plugin-uglify';
export default {
entry: 'app/main.js',
format: 'cjs',
plugins: [
npm({
jsnext: true,
main: true,
}),
babel({
exclude: 'node_modules/**',
presets: [ 'es2015-rollup' ],
}),
commonjs(),
//uglify(),
],
dest: 'static/js/app.js',
}
main.js
import Application from './application'
import pluralize from 'pluralize'
var text = Application.run()
console.log(`Testing encryption: ${text}`)
console.log(`Testing pluralization: ${pluralize('person')}`)
application.js
import crypt from 'crypto-js'
var Application = {
run() {
var ciphertext = crypt.AES.encrypt('Testing encryption...', 'password')
var bytes = crypt.AES.decrypt(ciphertext.toString(), 'password')
return bytes.toString(crypt.enc.Utf8)
}
}
export default Application
Running the above will generate the errors.
Just speculating: Maybe is a bug of the rollup and/or crypto.
I have a similar error when trying to run a js function in Node Red, the js is ok when I run it locally but it throws TypeError: Cannot read property 'split' of undefined when runs remotely.
The only thing that my code have in common with yours is that both uses cryptography, specifically crypto-js 3.1.2 rollup "hmac-sha256.js" and the code is not imported but raw.
Even after deleting the only instance of 'split' yet I can't solve it (but keeps running locally)

Categories