I have an Ember CLI project, and one of my routes starts like:
import Ember from 'ember';
import Activity from '../models/activity';
var ACCESS_KEY = gOptions.access_key;
export default Ember.Route.extend({
...
I have a file vendor/local_config.js that contains machine-specific information, and is in the .gitignore to be omitted from the repository. It contains:
var gOptions = {
access_key: 'abcde'
};
In my ember-cli-build.js file, I have imported this file like so:
app.import('vendor/local_config.js');
Running this application, everything works perfectly. The gOptions is universally available.
However, JSHint complains:
routes/activities.js: line 4, col 25, 'gOptions' is not defined.
I'm very new to Ember - what is the preferred method for updating my route to ensure that the vendor-provided classes are properly resolved/detected when JSHint is examining my routes/models?
Or: Is there a superior/Ember-like way of providing locally-defined environment variables, that should not be shared to a team?
If you're importing a global library, you just need to add the library to your JSHint configuration. From the Ember-CLI documentation:
Note: Don’t forget to make JSHint happy by adding a /* global MY_GLOBAL */ to your module, or by defining it within the predefs section of your .jshintrc file.
So just open up your .jshintrc file and add gOptions to the predefs array.
Related
I am writing a plugin for existing JavaScript app - Forge Autodesk.Viewing
After version 6 they have included THREE.js inside of their app bundle.
Right now I'm able to use it with my plugin like this:
declare var THREE:any;
but I lose all types, so I install three.js by:
npm install --save three
I'm able to use THREE, and import it, but I don't need to Import it as I already have it in my main app. What I need to do is to reference types, so I tried to do something like this:
declare var THREE:THREE;
//Cannot use namespace 'THREE' as a type.
Then I tried to:
/// <reference types='three' /> which works fine, but:
const planes:THREE.Plane[] = []; //this line is okey
planes.push(new THREE.Plane()); //but this says
//'THREE' refers to a UMD global,
// but the current file is a module.
// Consider adding an import instead.
Tsc insists that we should import it:
import * as THREE from 'three';
It compiles without any issues, but when I launch the app it crash, because it's trying to get one more instance of THREE.js, which I do not provide because I have it inside main app.
How to declare the correct reference and keep types to an namespace which is available at main JavaScript application?
There's a TypeScript definition file (.d.ts) for Forge Viewer that you should be able to use together with a THREE.js definition file: https://forge.autodesk.com/blog/typescript-definitions-forge-viewer-and-nodejs-client-sdk-now-available.
Ypu need to import THREE module:
Like this:
import * as THREE from 'three'; // or import THREE from 'three';
or
var THREE = require('Three').
and use webpack or other module loader (!)
If you want to manually include THREEJS distribution file and use TS bindings (type definitions) without modules (not recommended) - you can include Three.d.ts file to your project (with other THREEJS d.ts files) and use it with three slashes ref. For example:
/// <reference path="..\..\node_modules\#types\three\index.d.ts" />
Note: don't import THREE namespace with "import" or "require" in this case.
If you have issue such as:
'THREE' refers to a UMD global, but the current file is a module. Consider adding an import instead.
(about UMD)
You may try to use option in tsconfig.json:
"compilerOptions": {
"allowUmdGlobalAccess": true,
(about config options)
This will give compiler access to UMD global, so you do not need to import or reference such modules in that case.
And it's exact the case with three.js They alredy add THREE namespace as module to UMD global scope. So if you need to include this module you should import. If you want only reference you could use this option. If typescript doesn't recognize this option in config just update your typescript.
npm install typescript
Thank you dear SalientBrain and dear Petr Broz for your attention and help.
Is it possible to configure ESLint in WebStorm so functions, variables, etc. are parsed also from files in the same folder? In my build process, I concatenate all files in the same folders into big closures, for example:
src/
main/ ===> "main.js"
api.js
init.js
ui.js
constants.js
.
.
renderer/ ===> "renderer.js"
core.js
events.js
I would like ESLint to treat all those files just like one, so I don't get "undef" errors for things that are defined.
If it can't be done automatically, I wouldn't mind to create a manual configuration specifying all those files if that is possible.
EDIT: Why I don't (can't) use modules? TLDR- legacy code and project requirements.
I need to minify all code. Current closure compiler can transpile ES6 into ES5, but I found some ES6 features very prone to produce broken code. So I am forced to use ES5.
As I need ES5. I would only be able to use require() to use modules. Now that's a problem, as require() is a dynamic include and it impacts performance on my context (big electron app for modest power devices)
So to answer #Avin_Kavish, I agree what I do is "technically non conforming", but at the end of the build process it is, because each folder has been grouped into a file. That file is the module or the script. To group the files I use a Gradle plugin https://github.com/eriwen/gradle-js-plugin, I inject a "closure header" and a "closure footer", and all the files in between in the order I want.
Despite the inconvenience, at the end I get super-compact nodeJS code, with all methods obfuscated, etc.
I ended up using #Patrick suggestion, thanks for that!
EDIT 2
WebPack + Electron-WebPack turned out to be what I was looking for.
BTW- I think the proper way to do this is if EsLint would allow a "folder" sourceType.
You didn't provide code examples in your question, but I assume you do something like this:
api.js
const api = {
fetchData() {
// some code that fetches data
}
};
core.js
const core = {
init() {
api.fetchData();
}
};
The ESLint rule that causes errors when you lint these JavaScript modules is the no-undef rule.
It checks for variables that are used without having been defined. In the code example core.js above, this would be api, because that is defined in another module, which ESLint doesn't know about.
You don't care about these errors, because in your actual JS bundle used in production, the code from api.js and core.js is concatenated in one bundle, so api will be defined.
So actually api in this example is a global variable.
The no-undef rule allows you to define global variables so that they won't cause errors.
There are two ways to do this:
Using Comments
At the beginning of your core.js module, add this line:
/* global api */
Using the ESLint Config
As explained here – add this to your .eslintrc file:
{
"globals": {
"api": "writable"
}
}
Side Note
As some commenters to your question pointed out, it would probably be better to use import and export statements in the modules, together with a module bundling tool like webpack to create one bundle from your JavaScript modules.
A physical JavaScript file with an import/export statement is a module by the standard. A single .js file without import/export is a script by the standard. What you are trying to do is non-conforming to this, there is no specification in ECMAScript that allows splitting a single script or module across several files. I do get where you are coming from, for example: C# has partial classes that allows you to split a class across multiple files. But trying to replicate this without a standard syntax is not wise. Especially, when import/export can and will do the job for you
For example, with the following assumptions, your main.js can be refactored to,
constants.js // <--- constants
ui.js // <--- logic to build UI
api.js // <--- exposing public api
init.js // <--- setup code before use
// main.js
// If you name this index.js you can import it as 'src/main' instead of 'src/main/main.js'
import { A,B } from './constants'
import { api } from './api'
import { displayUi } from './ui'
import { init } from './init'
init(A);
displayUi(B);
export { api } // <-- re-expose public api
I'm converting an existing AngularJS application to a hybrid application, to begin incrementally upgrading it to Angular 4. I want to understand how Angular 4 needs to be referenced in the existing AngularJS. In the existing AngularJS application, it's clear how the AngularJS framework is being loaded - it's simply an included script file:
<script src="/angular/angular.js"></script>
To familiarise myself with an up to date Angular version, I've created a separate 'quickstart' Angular 5 application using the Angular quickstart guide at https://angular.io/guide/quickstart, and I can run it using:
ng serve --open
When I look at the project files though, I'm not seeing where the angular framework is actually being loaded. There is no script being included anywhere in the src/index.html file for that application, it simply declares an directive for a component (app.component.ts) that looks like this:
import { Component } from '#angular/core';
#Component({
selector: 'app-root',
templateUrl: './app.component.html',
styleUrls: ['./app.component.css']
})
export class AppComponent {
title = 'Test Angular 5 app';
}
Where does the import actually import from? The Angular documentation says about the index.html file that it is:
The main HTML page that is served when someone visits your site. Most
of the time you'll never need to edit it. The CLI automatically adds
all js and css files when building your app so you never need to add
any or tags here manually.
So what is happening here? How is #angular/core actually being referenced?
When using Angular CLI, we have a lot of moving parts. To really understand what's happening, we should review some of the core technologies used in Angular CLI as they relate to output files and module resolution:
TypeScript -- will transpile your TypeScript code to ES5 or ES6 (most commonly ES5). The transpiled code will use CommonJS modules by default if the target is ES5. If the target is ES6, then ES6 modules will be used.
Webpack -- will take your transpiled TypeScript code and build a dependency graph of your import / exports based on the entry points defined in webpack.config.js. This dependency graph will include all of your modules and it will package these modules into 1+ bundles, depending on the configuration. This is the step where the #angular/core dependency (which lives in node_modules/#angular/core) is processed by Webpack and added to a bundle that can be accessed at runtime.
You can load the generated bundles into the page by including the generated files in your HTML. In the case of JS files, you load them using the script tag like you did with AngularJS.
Because we are using Angular CLI, there are a lot of configurations within Webpack that are set up by default, so we will have multiple generated bundles.
I just created an empty Angular CLI project and inspected the HTML to see the 5 generated bundles:
At the core of the complexity when comparing Angular project files with AngularJS files is that Angular code is transformed through multiple build steps, while AngularJS code is normally used out-of-the-box with ES5. If you were to add Webpack and TypeScript to your AngularJS build, you would see something very similar to the Angular output.
Where is Webpack Configured in Angular CLI?
Under the hood, Angular CLI uses Webpack to build your project and bundle your files.
The Angular team chose not to expose a configurable Webpack configuration file for Angular CLI, but they did add ng eject, which generates a webpack.config.js that matches the Webpack build of your project. The downside of ejecting is that you will no longer use ng serve to serve your project. Instead, you'll use npm run build && npm run start (you'll see these scripts added to your package.json after you eject), which will build and serve your project based on the generated webpack.config.js file.
This feature is essential if you need to make custom modifications to the default Webpack build.
Read more about ng eject here.
Where are the generated bundles?
If you are using ng serve, you won't see any of your generated files because these are being served from memory and not from disk (to speed up development when files are constantly changing), so your generated files are not located in any folder.
If you want to see your generated files, you can run ng build, which will create a dist folder with the index.html and associated assets/bundles.
Note that by default, all commands that build or serve your project will delete your dist folder unless you pass the --no-delete-output-path when building/serving.
You need to understand how imports and exports work in TypeScript
From TypeScript Docs
Exporting a declaration
Any declaration (such as a variable, function, class, type alias, or
interface) can be exported by adding the export keyword.
Validation.ts
export interface StringValidator {
isAcceptable(s: string): boolean;
}
ZipCodeValidator.ts
export const numberRegexp = /^[0-9]+$/;
export class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
Export statements
Export statements are handy when exports need to be renamed for
consumers, so the above example can be written as:
ZipCodeValidator.ts
class ZipCodeValidator implements StringValidator {
isAcceptable(s: string) {
return s.length === 5 && numberRegexp.test(s);
}
}
export { ZipCodeValidator };
export { ZipCodeValidator as mainValidator };
Import
Importing is just about as easy as exporting from a module. Importing
an exported declaration is done through using one of the import forms
below:
Import a single export from a module
import { ZipCodeValidator } from "./ZipCodeValidator";
let myValidator = new ZipCodeValidator();
imports can also be renamed
import { ZipCodeValidator as ZCV } from "./ZipCodeValidator";
let myValidator = new ZCV();
Import the entire module into a single variable, and use it to access the module exports
import * as validator from "./ZipCodeValidator";
let myValidator = new validator.ZipCodeValidator();
Since you followed the Angular quickstart guide you must have used npm which is used to install your modules and dependencies.
If you read angular.io/npm
#angular/core: Critical runtime parts of the framework needed by every
application. Includes all metadata decorators, Component, Directive,
dependency injection, and the component lifecycle hooks.
And by default npm stores all your dependencies in node_modules directory.
So you are importing Component from #angular/core which lives inside node_modules directory.
I wrote a library written in typescript and I use webpack to bundle it into a single, minified js file that exports the library as one global variable. I want to distribute these typings for the developers on my team. They are not typescript users so they would be importing the library itself via a <script src="..."> tag and they would import the typings using the triple slash reference directive /// <reference path="..." /> or typings or something similar.
The question is then:
How do I enable typings (i.e., the intellisense) for my library for the js developers that use vs code? How do I declare that there is one global variable that exports the values that my entry point does?
I have enabled the declarationDir and declartion compiler options to create create the declaration files (*.d.ts) for all my typescript source files but these declarations don't declare that there is a global variable available with the methods.
I've tried manually creating a index.d.ts declaration in the same folder where the bundle and minified index.js file is but I can't get it to work.
Here is what I tried:
import * as myModule from './typings/src/';
declare module TheGlobalVariable {
// how do I declare that `TheGlobalVariable` has the same methods
// as `myModule` exports?
export = myModule; // doesn't work
}
where ./typings/src/index.d.ts is the generated declaration file for the entry point of this library and TheGlobalVariable is the name of the global variables webpack exports.
Any ideas?
Oh I figured it out.
In the same folder where my bundled js library file is (index.js), I added a file index.d.ts. Here is that file:
export * from './src';
export as namespace OntologyStore;
The key enabler is the export as ....
Refer to this module template and this documentation on library structures.
Also change the webpack libraryTarget to 'umd'. UMD modules check to see if there is a module loader and if there isn't it will export to a global. Also I didn't have to use the declaration or declarationDir compiler options.
Use this structure, https://github.com/botika/typescript-package.
Only follow a README.md
I'm trying to add custom functionality to extend lodash (note lodash
is npm'ed in). But I keep getting a resolve error.
I've added a new file called lodash-mixins.js to my test project scripts folder e.g: project/frontend/src/web/Scripts/
var _ = require('lodash');
_.mixin({
mixinLoaded function () { console.log("lodash mixins are loaded");}
});
module.exports = _;
Overview Folder Structure (simplified)
project/frontend/src/web
...frontend.web.csproj
...angular-cli.json
project/frontend/src/web/Scripts/
...lodash-mixins.js
project/frontend/src/web/app/
...app.module.ts
I've manually added my "lodash-mixins.js" to the "angular-cli.json"
"apps": [
{
"scripts": [
"../node_modules/jquery/dist/jquery.min.js",
etc
"../node_modules/lodash/lodash.min.js",
"../Scripts/lodash-mixins.js", // <<<<< not picking up
"../Scripts/global-error-handler.js",
],
Test by changing existing reference in one of my test.service.ts
from:
"import * as _ from 'lodash';"
to:
"import * as _ from 'lodash-mixins';"
I've rebuilt my c# solution for good measure.
Run in CLI: $ng build --watch
ERROR in project/frontend/src/web/app/test/services/test.service.ts
Module not found: Error: Can't resolve 'lodash-mixins
Any ideas?
You're confusing two different things here.
1) The "scripts" config for Angular CLI tells WebPack to include those JavaScrip files in the output bundle. It doesn't add those as importable modules. They get loaded as if you added <script src="..."> tags to your HTML file (not exactly accurate, but gives the idea).
2) When you import using TypeScript it searches for the module. The reason it's giving the error is because the file isn't in one of the search paths. Had it actually found the file. It would have loaded it twice. Since you've already added it to the "scripts" config option.
JQuery, Lodash, etc.. etc.. can be loaded using modules or just plain global variables. When you add it to the "scripts" config, then I think this tells WebPack to load it in the global browser space.
When you use "import _ from 'lodash'" in TypeScript. You're actually resolving to the "#types/lodash" module which people often install so that TypeScript knows about the interface for lodash. When WebPack bundles everything for the browser it swaps out the #types for the real reference to the lodash instance.
So you have a couple of options.
1) Continue with what you've done. Add TypeScript definition file in the import path for your new module named "lodash-mixin". That module just defines the new interface with the new methods. The import will find that file and use it to compile the TypeScript, but the WebPack bundle will load your JS file in it's place.
2) Remove your "lodash-mixin" from the "scripts" config, then import using a relative path import _ from '../Scripts/lodash-mixins'. This is what I usually do. Note: You might have to add the file extension ".js"
3) Add your "Scripts" folder to your tsconfig.json as one of the type roots. This allows you to just use import _ from 'lodash-mixins'.
4) There is a way (and I forget exactly how), but you can tell TypeScript to alias lodash to your lodash-mixin so that all imports use that type instead. I don't recommend this approach.