I have a typescript .ts file with the following.
I am using webpack version 2.2.1
import { module } from "angular";
import "typeahead";
class TypeAheadController {
static $inject = ["$log", "$timeout", "$http", "$interpolate"];
constructor(
public $log: ng.ILogService,
public $timeout: ng.ITimeoutService,
public $http: ng.IHttpService,
public $interpolate: ng.IInterpolateService) {
// do stuff with Typeahead / Bloodhound.
var bloodhoundSuggestions = new Bloodhound({
datumTokenizer: _ => Bloodhound.tokenizers.obj.whitespace(_[this.inputValue]),
queryTokenizer: Bloodhound.tokenizers.whitespace,
}
because the typeahead definitions are in #types/typeahead and the implementation is in typeahead.js, it's necessary to alias the location in webpack.config.js
globule = require("globule");
var configuration = {
context: __dirname,
resolve:
{
alias: {
typeahead: "typeahead.js"
}
},
entry: [
...globule.find("client/*.ts", { srcBase: "wwwroot/js/admin" })
],
output: {
filename: "./wwwroot/js/admin/admin.js"
},
module: {
rules: [
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
console.log(configuration);
module.exports = configuration;
Unfortunately in the resulting generated javascript file, Bloodhound is undefined.
Webpack seems to include and use the relevant require (323), but it's clearly not working as Bloodhound is undefined.
in the output file, the require is present just before the controller is defined.
Object.defineProperty(exports, "__esModule", { value: true });
var angular_1 = __webpack_require__(16);
__webpack_require__(323);
/**
* initialise and use a type ahead to find suggestions.
*/
var TypeAheadController = (function () {
much further down the file, I find 323.
/***/ }),
/* 323 */
/***/ (function(module, exports, __webpack_require__) {
/* WEBPACK VAR INJECTION */(function(setImmediate) {var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;var __WEBPACK_AMD_DEFINE_ARRAY__, __WEBPACK_AMD_DEFINE_RESULT__;/*!
* typeahead.js 0.11.1
* https://github.com/twitter/typeahead.js
How do I fix the undefined Bloodhound?
This package is a quirky one. It is named typeahead.js, but the "main" entry in package.json actually exports the Bloodhound function and attaches the typeahead function to jQuery.fn. To make matters worse, its #types package has the wrong name (missing .js) and is written using a declaration format that expects you to import from "bloodhound". This is painful but can be worked around.
Here are the steps you need to take:
Install typeahead.js with npm (since you are using Webpack)
npm install --save typeahead.js
install the #types package (note no .js, this is annoying)
npm install --save #types/typeahead
Remove the alias it is not needed. Specifically, the following line must be removed from webpack.config.js:
typeahead: "typeahead.js"
Create a declaration file ambience.d.ts (name is not significant)
declare module "typeahead.js" {
export = Bloodhound;
}
Unfortunately the code above refers to the global Bloodhound which the #types/typeahead unconditionally declares. Fortunately it will not be global at runtime.
Adjust your code so it is roughly as follows
import angular from "angular";
import Bloodhound from "typeahead.js"; // note the `.js`
class TypeAheadController {
static $inject = ["$log", "$timeout", "$http", "$interpolate"];
constructor(
readonly $log: angular.ILogService,
readonly $timeout: angular.ITimeoutService,
readonly $http: angular.IHttpService,
readonly $interpolate: angular.IInterpolateService) {
// do stuff with Typeahead / Bloodhound.
const bloodhoundSuggestions = new Bloodhound({
datumTokenizer: _ => Bloodhound.tokenizers.obj
.whitespace(_[this.inputValue]),
queryTokenizer: Bloodhound.tokenizers.whitespace
});
}
}
Related
I am getting the following error from webpack.
ERROR in ./wwwroot/js/admin/infrastructure/typeaheadComponent.ts
Module not found: Error: Can't resolve 'typeahead' in ...
I have the following installed
npm install typeahead.js
npm install #types/typeahead
My typescript is as follows, using node module resolution.
import { module } from "angular";
import "typeahead";
// necessary to import typeahead into JQuery, as otherwise
// typeahead below is not defined.
class TypeAheadController {
foo(e) {
$(e).typeahead(...)
}
}
this generates javascript as follows:
"use strict";
var angular_1 = require("angular");
require("typeahead");
var TypeAheadController = (function () { ...
My webpack.config.js is as follows:
module.exports = {
context: __dirname,
entry: [
"./app.ts",
"./tab.ts",
"./client/clientService.ts",
"./client/clientSearchComponent.ts",
"./infrastructure/messageComponent.ts",
"./infrastructure/typeaheadComponent.ts",
"./url.ts"],
output: {
filename: "./wwwroot/js/admin/admin.js"
},
devtool: "source-map",
module: {
rules: [
{ test: /\.ts$/, use: 'ts-loader' }
]
}
};
imported into a gulp task.
How do I specify that typeahead is located in node_modules/typeahead.js/dist/typeahead.bundle.js
The module is called typeadhead.js so you also need to import typeahead.js, not typeahead.
import "typeahead.js";
The import is always the same as the name you use to install it with npm. And it's not even special, it simple looks into node_modules and finds the directory with the given name. Then it looks into package.json and imports the file specified in the main field. See also Node.js - Folders as Modules.
You could use resolve.alias to change the name of the import, but there is not really a good reason for doing that in this case.
I resolved this by making the following changes.
You need to import Bloodhound and Typeahead seperately. To do this edit your webpack.config.js
resolve: {
extensions: ['.js', '.ts'],
alias: {
typeahead: 'corejs-typeahead/dist/typeahead.jquery.min.js',
bloodhound: 'corejs-typeahead/dist/bloodhound.min.js'
}
},
And then in your .ts file:
import "typeahead";
import * as Bloodhound from "bloodhound";
You could solve this using aliasing. Minimal example of what to change in your webpack.config.js:
module.exports = {
/* ... everything you currently have */
resolve: {
alias: {
typeahead: 'typeahead.js'
}
}
}
I am following this tutorial to learn webpack. It's out-dated, since the tools referenced are upgraded in my case. My versions are as follows.
ts-loader ^1.3.3
tsd ^0.6.5
typescript ^2.1.5
webpack ^1.14.0
After successfully creating a bundle.js file, I seem to be having problems accessing/referencing my TypeScript to JavaScript classes. My tsconfig.json looks like the following.
{
"compilerOptions": {
"module": "commonjs",
"sourceMap": true,
"declaration": true
},
"include": [
"src/**/*"
]
}
My wepack.config.js looks like the following.
var webpack = require('webpack');
module.exports = {
entry: './src/app.ts',
output: {
filename: 'bundle.js'
},
devtool: 'source-map',
resolve: {
extensions: ['', '.webpack.js', '.web.js', '.ts', '.js']
},
module: {
loaders: [
{ test: /\.ts$/, loader: 'ts-loader' }
]
}
}
To transpile the TypeScript code, I simply type in tsc. To bundle all the code into bundle.js, I simply type in webpack. (tsc and webpack are installed globally).
In my src directory, I only have 2 simple classes. The car.ts looks like the following.
export class Car {
public sayHi(): void {
console.log('car says hi');
}
}
The app.ts looks like the following.
import { Car } from './car'; //alternatively, import Car = require('./car');
let car = new Car();
car.sayHi();
I then load this bundle.js into a browser in a HTML page as follows.
<html>
<head>
<title>Test Webpack</title>
</head>
<body>
<script src="bundle.js"></script>
</body>
</html>
I can see in the (Chrome) JavaScript console the log message "car says hi". But how do I create a new instance of Car? The following attempts at the JavaScript console doesn't work.
var c1 = new Car(); //Uncaught reference error
var c2 = new car.Car(); //Uncaught reference error
Inside bundle.js, I do see this code/modules being generated (snippet).
/* 0 */
/***/ function(module, exports, __webpack_require__) {
"use strict";
var car_1 = __webpack_require__(1);
var car = new car_1.Car();
car.sayHi();
/***/ },
/* 1 */
/***/ function(module, exports) {
"use strict";
var Car = (function () {
function Car() {
}
Car.prototype.sayHi = function () {
console.log('car says hi');
};
return Car;
}());
exports.Car = Car;
/***/ }
My end goal is to get all TypeScript files in the src directory transpiled with (TypeScript) mappings into a single bundle.js as a module so I can use/reference the code (as JavaScript in a browser) as follows.
var car = new mylib.Car();
var plane = new mylib.Plane();
car.sayHi();
plane.sayHi();
Any ideas on what I'm doing wrong?
You need to set the output.library setting:
output: {
...
library: 'MyLibrary'
}
Then you can use it this way in the console:
var x = new MyLibrary.Car();
Webpack documentation (output.libray)
Similar answer on SO
What you should do is let webPack handle the transpilation for you as well.
Using a TypeScript loader.
What webpack does is making modules (this last file you put, everything is inside a function with context and without the Name you gave on a global space).Any global names are bound to the scope (or changed if you ugligy) so You can't access them from the console. Make webpack use the typescript loader and you can reference the car simply requiring or importing the file.
I'm converting a javascript project with Angular 1.x to WebPack and TypeScript (using ts-loader). I got it mostly working, but I'm running into trouble when ts-loader seems to be optimizing my scripts out of the bundle when the exports are not directly used.
Here's a sample project demonstrating the issue (npm install, webpack, then load index.html and watch the console).
https://github.com/bbottema/webpack-typescript
The logging from ClassA is showing up, but angular is reporting ClassB missing (provider). If you look in bundle.js you'll notice ClassB missing entirely. The difference is ClassA begin use directly after importing, and ClassB is only referenced by type for compilation.
Is it a bug, or is there a way to force ClassB to be included? Or am I going about it wrong? Angular 2 would probably solve this issue, but that's a step too large right now.
Relevant scripts from the project above:
package.json
{
"devDependencies": {
"typescript": "^1.7.5",
"ts-loader": "^0.8.1"
},
"dependencies": {
"angular": "1.4.9"
}
}
webpack.config.js
var path = require('path');
module.exports = {
entry: {
app: './src/entry.ts'
},
output: {
filename: './dist/bundle.js'
},
resolve: {
root: [
path.resolve('./src/my_modules'),
path.resolve('node_modules')
],
extensions: ['', '.ts', '.js']
},
module: {
loaders: [{
test: /\.tsx?$/,
loader: 'ts-loader'
}]
}
};
tsconfig.json
{
"compilerOptions": {
"target": "es5",
"module": "commonjs"
},
"exclude": [
"node_modules"
]
}
index.html
<!doctype html>
<html ng-app="myApp">
<body>
<script src="dist/bundle.js"></script>
</body>
</html>
entry.js
declare var require: any;
'use strict';
import ClassA = require('ClassA');
import ClassB = require('ClassB');
var a:ClassA = new ClassA(); // direct use, this works
var angular = require('angular');
angular.module('myApp', []).
// this compiles as it should, but in runtime the provider will not be packaged and angular will throw an error
run(function(myProvider: ClassB) {
}
);
ClassA.ts
// this line will be logged just fine
console.log('ClassA.ts: if you see this, then ClassA.ts was packaged properly');
class ClassA {
}
export = ClassA;
ClassB.ts
declare var require: any;
// this line is never logged
console.log('ClassB.ts: if you see this, then ClassB.ts was packaged properly');
class ClassB {
}
var angular = require(angular);
angular.module('myApp').service(new ClassB());
export = ClassB;
Turns out you have to signal WebPack to explicitly include a module by adding an extra require call without import statement.
I'm not ready to mangle my .ts files by adding duplicate imports, so I made a generic solution for that using the preprocessor loader:
{
"line": false,
"file": true,
"callbacks": {
"fileName": "all",
"scope": "line",
"callback": "(function fixTs(line, fileName, lineNumber) { return line.replace(/^(import.*(require\\(.*?\\)))/g, '$2;$1'); })"
}]
}
As a proof of concept, this regex version is very limited it only support the following format:
import ClassA = require('ClassA');
// becomes
require('ClassA');import ClassA = require('ClassA');
But it works for me. Similarly, I'm adding the require shim:
{
"fileName": "all",
"scope": "source",
"callback": "(function fixTs(source, fileName) { return 'declare var require: any;' + source; })"
}
I made a sample project with this solution.
I want to build a quick nodejs script to package a Typescript app as SystemJS modules, a lot like what Angular2 bundles look like.
I tried different configurations but I can't seem to put my finger on it, and haven't found clear enough documentation as of yet.
Note that for this "test", I am not using Gulp or Jspm at all, just systemjs-builder for the time being (and don't plan on using jspm at all either)
Here's what my "project" looks like:
---- Project's Root
-------- index.ts // export * from './modules/index' and eventually more
-------- modules
------------ index.ts // export * from './menu/index'
------------ menu
---------------- menu.component.ts // export class
---------------- menu.service.ts // export class
I want to package this under a single file, where I will have multiple SystemRegister modules that can be consumed in an app thereafter
I tried the following without success:
var Builder = require('systemjs-builder');
// optional constructor options
// sets the baseURL and loads the configuration file
var builder = new Builder('./modules');
builder.bundle('./modules/index.ts', {
/* SystemJS Configuration Here */
baseURL: './modules',
transpiler: 'typescript',
typescriptOptions: {
"module": "system",
"emitDecoratorMetadata": true,
"experimentalDecorators": true
},
defaultExtension: 'ts',
packages: {
'modules': {
defaultExtension: 'ts'
}
}
}, 'infrastructure.js')
.then(function() {
console.log('Build complete');
})
.catch(function(err) {
console.error(err);
})
First of all, the defaultExtension options doesn't seem to work at all
So when I do import {something} from 'filePath'; (without extension), it tries to load filePath, instead of filePath.ts;
Second, if I try adding the .ts extension in my imports (which I don't want to do), it complains that the code is invalid (unexpected token #, unexpected token menuItem and so forth)
Anyone have a good example or some explanations on how this is supposed to work?
Thank you
here you have an example: angular typescript skeleton
build task looks like this:
const path = require('path');
const Builder = require('jspm').Builder;
const builder = new Builder();
const packageJson = require(path.join(config.projectDir, 'package.json'));
return beginBuild()
.then(buildSFX)
.catch((err) => console.log('Build Failed', err));
function beginBuild() {
builder.reset();
return builder.loadConfig(path.join(config.projectDir, packageJson.jspm.configFile))
}
function buildSFX() {
const appName = packageJson.name;
const distFileName = `${appName}.min.js`;
const outFile = path.join(config.distDir, distFileName);
const moduleName = 'app';
const buildConfig = {
format: 'global',
minify: true,
sourceMaps: true
};
return builder.buildStatic(moduleName, outFile, buildConfig);
}
and jspm conf looks like this:
System.config({
defaultJSExtensions: true,
transpiler: "typescript",
typescriptOptions: {
"tsconfig": "src/tsconfig.json"
},
paths: {
"github:*": "vendor/jspm_packages/github/*",
"npm:*": "vendor/jspm_packages/npm/*",
"app": "src/index"
}
/// ...
}
Why do you want to bundle typescript? Bundling is a method used for optimizing the delivery of source code to the browser. The browser doesn't know typescript, it only knows javascript (unless you do on the fly transpiling).
I recently saw an example of how requirejs bundles work(http://vimeo.com/97519516) and got excited. So I'm trying to get the bundling feature to work with my setup and am banging my head on the wall.
Troubleshooting done so far.
The main.js file will be created properly without bundling and defining the comp1 component in the main.js top depenendecy. But when I nest the comp1 dependency and add the bundling options the page1 and page2 bundle never gets created. So it doesn't seem to be an issue with the rest of the configuration. I have also tried this a few different ways. Removing the module and and putting everything in the root of the config. Moving the bundles to the all-config.js. Any ideas on what I could be doing wrong.
all-config.js
(function() {
if (typeof requirejs != 'function') {
requirejs = function(config) { requirejs = config; };
}
requirejs({
pathes: {
comp1: "path1",
comp2: "path2,
router: "path3"
}
});
build.json
{
"baseUrl": "./",
"appDir": "../src/main/webapp",
"dir": "build",
"mainConfigFile": "app/all-config.js",
modules: [
{
"include": [
"app/lib/require/require.js"
],
bundles":{
"page1" : ["comp1"],
"page2" : ["comp2"]
}
}
]
}
main.js
require(['jquery', 'router'],function($){
//load app
require(["comp1"],function(comp1){
var app = new comp1();
});
});
Gruntfile
module.exports = function(grunt){
var stripper = require('strip-json-comments');
var buildOptionsFile = grunt.file.read( 'build.json' );
var buildOptions = JSON.parse(stripper(buildOptionsFile) );
grunt.initConfig({
requirejs: {
compile: {
options: buildOptions
}
}
});
.....
};
Any ideas on how to get this to work?