Importing in Javascript - javascript

When one imports a specific value from another file, does the entire file that has been exported from run in the file importing? For example if I wanted to import the function "hello" from file b, into file a, would file b run in file a?
An example being:
File A:
import {func} from 'fileB.js';
File B:
let func = function(){...}
console.log(`Hello`);
export {func};
Would Hello appear in the console of file A, and if it would, under what circumstances. For example, would it be when the import statement is run, or when the func is called. If it would not run, are there any ways to make it so it does. For example if I exported the entire file (if that's possible) would the Hello appear under certain circumstances?

The imported file will be run. An easy way to both understand and remember this is dynamic exports:
export let Foo;
if (window.Foo === undefined) {
Foo = class Foo { ... }
} else {
Foo = window.Foo;
}
In order to know what was exported to begin with, the code needs to be run. Otherwise, it would be equal to solving the halting problem.

if you are using webpack import or require
declare like this
const Logger = function() {
}
export { Logger };
use it
import { Logger } from '../class/Logger';
let logger = new Logger();

Related

How do I import all files in a folder as an module and export all as an object? [duplicate]

This question already has answers here:
Is it possible to import modules from all files in a directory, using a wildcard?
(14 answers)
Closed 3 years ago.
I'm trying to make every file exported as module in a certain folder, to be imported used as a single object. How should I do this?
I had made a javascript plugin with a long long single file. and I'm now trying to make it to be separated by several files. I'm using Webpack for it.
And, in my plugin code, there was a huge object named "fn" which contains 40~50 functions with 200~300lines each. To split them, I made a folder named "fn" and puts each functions as each files. Each name of file and name of function are identical. so function named "foo" is exported by default in a file named "foo.js" in "fn" folder.
And now I'm trying to import them all and export it as a single object like this.
src/fn/foo_or_whatever.js
export default function aFunctionWhichIUse(){
// return whatever;
}
src/fn/index.js
import foo from './foo';
import bar from './bar';
import fooz from './fooz';
import baz from './baz';
import kee from './kee';
import poo from './poo';
import tar from './tar';
import doo from './doo';
.
.
.
import foo_or_whatever from './foo_or_whatever';
export default {
foo,
bar,
fooz,
baz,
kee,
poo,
tar,
doo,
.
.
.
foo_or_whatever,
}
src/index.js
import fn from './fn'
But I don't think it does make sense. Now I have to type same word every four times just to add a function in a "fn" object.
I've found that importing all files from folder is not supported in import/export system. then, What should I use and how should I write code to do that?
Is there some way to do like this? I don't matter whether the way is or not import/export system, as long as if the way works on the webpack.
src/index.js
import * as fn from './fn';
const fs = require('fs');
const path = require('path');
const basename = path.basename(__filename);
const functions = {}
fs
.readdirSync("./")
.filter(file => (file.indexOf('.') !== 0) && (file !== basename) && (file.slice(-3) === '.js'))
.map((file) => {functions[file.slice(0, -3)] = require(path.join(__dirname, file})))
module.exports = functions;
Would something like this help?
Doing this automatically might seem a good idea, but with webpack it's going to then need special plugins to handle it.
But another alternative if the repeating part is your main concern I might have another idea for you.
Webpack can handle commonjs modules too. So what you could do is ->
module.exports = {
foo: require('./foo'),
bar: require('./bar'),
fooz: require('./fooz'),
baz: require('./baz'),
kee: require('./kee'),
poo: require('./poo'),
tar: require('./tar'),
doo: require('./doo')
};
Another advtange is if say ou wanted to temporyly replace baz, you could just change the require to ./baz-temp etc.
Also it's always a good idea to name files in lowercase, so if you wanted to include a class doing this automatically would be a problem,. But just do -> Foo: require('./foo') would be fine.

How do I import an IIFE-based JavaScript module into an Angular TypeScript app?

So I have a third-party SDK written as an oldschool IIFE based module. In other words it looks something like this:
var ThirdPartySDK = (function() {
var export = {};
// Add some methods to export
return export;
})();
You would then be expected to use it by referencing it on the global scope like this:
<html>
<body>
<script src="lib/ThirdPartySDK.js">
<script>
ThirdPartySDK.foo();
<\script>
<\body>
<\html>
I could still use it this way of course, but is that really the best practice with Angular and TypeScript? Is there some way to set things up with angular/TypeScript/webpack so that I can use a proper import statement? Something like this:
import { ThirdPartySDK } from '../lib/ThirdPartySDK.js';
ThirdPartySDK.foo();
The best way to have a proper import statement for the actual value of ThirdPartySDK is to refactor the script to a module that exports this value. The following snippet allows you to use the import statement as showed:
export const ThirdPartySDK = {
foo() { console.log('Doing foo'); }
};
For big libraries refactoring is not always that easy, so I see 2 approaches that do not involve too much refactoring:
1. Export the ThirdPartySDK variable
You could simply make a module out of the IIFE file by exporting the current IThirdPartySDK variable (returned by the IIFE), and then import it as you showed:
export const ThirdPartySDK = (function() {
var _export = {};
// Add some methods to export
return _export;
})();
Note that if you want to have some useful information about the shape of ThirdPartySDK you would have to add a type annotation to the const declaration, and if SomeType (see below) does not yet exist you'll have to write it yourself:
export const ThirdPartySDK: SomeType = (function() {
// ...
At this point Typescript will start to complain about the IIFE expression not being assignable to SomeType; the quick 'solution' to tell typescript to pretend the expression evaluates to a value of type SomeType using the as keyword:
export const ThirdPartySDK: SomeType = (function() {
// ...
})() as SomeType;
2. Keep the <script> tag and declare the variable
Another option it to keep the script tag, import nothing, and declare the variable and its expected type in typescript:
(But also in this case you might have to provide type definitions yourself)
interface SomeType {
// SDK type shape goes here...
}
declare const ThirdPartySDK: SomeType;
You can wrap the third-party SDK in a TypeScript module using a hack with eval.
Let's say that ThirdPartySDK.js looks like this:
var ThirdPartySDK = (function () {
var exports = {
foo: function () { console.log("Hello, world!"); }
};
return exports;
})();
You would then create a ThirdPartySDK-wrapper.ts module that looks something like this:
import * as fs from 'fs';
const script = fs.readFileSync('../lib/ThirdPartySDK.js').toString();
global.eval(script);
//#ts-ignore
export default ThirdPartySDK;
The #ts-ignore directive is required to keep the TypeScript compiler from complaining about not finding a declaration for the ThirdPartySDK variable (it is declared in the script executed through eval).
You can then import ThirdPartySDK through the wrapper module:
import ThirdPartySDK from './wrapper';
ThirdPartySDK.foo(); // Console output: "Hello, world!"
Note that this wrapper only works for applications running in Node.js, since it uses fs.fileReadSync to get the contents of the script.
If you're going to use it in a browser, you will need some other way to retrieve the script. You could probably use frameworks such as WebPack to bundle the ThirdPartySDK script as a string value that you can require in the wrapper module.

Resolving Export / Imports

I have a Namespacing.js with something like the following
(function(){
window.GlobalObject = {
foo : function() { console.log("bar"); }
}
})();
Then i have another MyScript.js
GlobalObject.newAttribute = { ... }
So i'm now bundling with webpack and i was tryng to use modules on this, but i couldnt manage to do so.
At Namespacing.js i added at the end:
export default GlobalObject;
Then i tryed to import it in MyScript.js
import GlobalObject from "Namespacing"
But then my webpack gets me an error
[14:58:44] GulpUglifyError: unable to minify JavaScript
Caused by: SyntaxError: Unexpected token: name (Kneat) (line: 1, col: 7, pos: 7)
Does any1 knows a good way of doing this export/import ?
To switch to import/export, you can't just add exports to your existing files, you need to change their structure (ever so slightly).
For instance, Namespacing.js would have either:
export const GlobalObject = {
foo : function() { console.log("bar"); }
};
...to export a named symbol GlobalObject. That would be imported like this:
import { GlobalObject } from './Namespacing.js';
(You could use an as clause if you wanted a different name locally in the importing module.)
Or you could export that object as the default export:
export default {
foo : function() { console.log("bar"); }
};
...and import it like this:
import GlobalObject from './Namespacing.js';
Note that in that case, you can use any name you want for GlobalObject in the module in which you're importing it (no need for as as clause).
Don't worry about the fact that it involves removing the IIFE; modules are inherently scoped, module code doesn't run at global scope.

Importing NodeJs into TypeScript compiling errors

I am trying to import a nodejs class into the typescript code using a declaration (d.ts) file.
To test it I created a simple nodejs class, declaration file and the typescript file that is going to import (consume) the nodejs class.
contents of TS_PROJECT/node_modules/newtest/index.js:
var HelloWorld = function() {
}
HelloWorld.hello2 = function() {
console.log("hello2");
}
HelloWorld.prototype.hello1 = function() {
console.log("hello1");
}
module.exports = HelloWorld;
So as you can see, it's a class that will have a member function "hello1" and a static function "hello2".
Now i'm trying to declare it in TS_PROJECT/newtest/index.d.ts:
declare module newtest {
export class HelloWorld {
hello1():void;
static hello2():void;
}
}
export = newtest;
And eventually trying to consume it from index.ts:
/// <reference path="./newtest/index.d.ts" />
import * as mymodule from "newtest";
Now I'm trying to instantiate the object and call the functions but no matter what i try it doesn't compile. I tried lots of variations, but none of them works. For example this:
// Doesn't Work
import HelloWorld = mymodule.HelloWorld;
let s: HelloWorld = new HelloWorld();
// It results with the following compile errors:
// error TS2339: Property 'HelloWorld' does not exist on type 'typeof "newtest"'.
// error TS2694: Namespace '"newtest"' has no exported member 'HelloWorld'.
I won't list all the rest of my variations. But i would appreciate if anybody could help me with proper declaration and instantiation of the object.
Thank you!
You don't need to import the type, since you're already importing everything from the module and naming it mymodule. You can do a couple things:
Import the class directly:
import { HelloWorld } from 'newtest'
let s:HelloWorld = new HelloWorld();
Access the type from the imported module:
import * as mymodule from 'newtest';
let s:mymodule.HelloWorld = new mymodule.HelloWorld();
Create a local variable reference to the type:
import * as mymodule from 'newtest';
const HelloWorld = mymodule.HelloWorld;
let s = new HelloWorld();
I think i figured the answer. In my example the declaration module "newtest" represents the exported nodejs object. Since the exported object is already HelloWorld object, it's wrong to define inside another class HelloObject.
So the simplest solution would be to have a nodejs code like:
var HelloWorld = function () {
}
HelloWorld.hello2 = function () {
console.log("hello2");
}
HelloWorld.prototype.hello1 = function () {
console.log("hello1");
}
// Only this part changed
module.exports.HelloWorld = HelloWorld;
This way we are exporting an object that has HelloWorld class inside. That fixed the compilation and the compiled JS.
Also another issue that I had, in the declaration file "newtest" has to be in quotes. Only that way it attaches the module to the nodejs package.
These to fixes solved my problem. But the question is still remains. What if I still have to use "module.exports = HelloWorld;"? How should I declare this module in this case?

TypeScript ES6 import module "File is not a module error"

I am using TypeScript 1.6 with ES6 modules syntax.
My files are:
test.ts:
module App {
export class SomeClass {
getName(): string {
return 'name';
}
}
}
main.ts:
import App from './test';
var a = new App.SomeClass();
When I am trying to compile the main.ts file I get this error:
Error TS2306: File 'test.ts' is not a module.
How can I accomplish that?
Extended - to provide more details based on some comments
The error
Error TS2306: File 'test.ts' is not a module.
Comes from the fact described here http://exploringjs.com/es6/ch_modules.html
17. Modules
This chapter explains how the built-in modules work in ECMAScript 6.
17.1 Overview
In ECMAScript 6, modules are stored in files. There is exactly one
module per file and one file per module. You have two ways of
exporting things from a module. These two ways can be mixed, but it is
usually better to use them separately.
17.1.1 Multiple named exports
There can be multiple named exports:
//------ lib.js ------
export const sqrt = Math.sqrt;
export function square(x) {
return x * x;
}
export function diag(x, y) {
return sqrt(square(x) + square(y));
}
...
17.1.2 Single default export
There can be a single default export. For example, a function:
//------ myFunc.js ------
export default function () { ··· } // no semicolon!
Based on the above we need the export, as a part of the test.js file. Let's adjust the content of it like this:
// test.js - exporting es6
export module App {
export class SomeClass {
getName(): string {
return 'name';
}
}
export class OtherClass {
getName(): string {
return 'name';
}
}
}
And now we can import it in these three ways:
import * as app1 from "./test";
import app2 = require("./test");
import {App} from "./test";
And we can consume imported stuff like this:
var a1: app1.App.SomeClass = new app1.App.SomeClass();
var a2: app1.App.OtherClass = new app1.App.OtherClass();
var b1: app2.App.SomeClass = new app2.App.SomeClass();
var b2: app2.App.OtherClass = new app2.App.OtherClass();
var c1: App.SomeClass = new App.SomeClass();
var c2: App.OtherClass = new App.OtherClass();
and call the method to see it in action:
console.log(a1.getName())
console.log(a2.getName())
console.log(b1.getName())
console.log(b2.getName())
console.log(c1.getName())
console.log(c2.getName())
Original part is trying to help to reduce the amount of complexity in usage of the namespace
Original part:
I would really strongly suggest to check this Q & A:
How do I use namespaces with TypeScript external modules?
Let me cite the first sentence:
Do not use "namespaces" in external modules.
Don't do this.
Seriously. Stop.
...
In this case, we just do not need module inside of test.ts. This could be the content of it adjusted test.ts:
export class SomeClass
{
getName(): string
{
return 'name';
}
}
Read more here
Export =
In the previous example, when we consumed each validator, each module only exported one value. In cases like this, it's cumbersome to work with these symbols through their qualified name when a single identifier would do just as well.
The export = syntax specifies a single object that is exported from the module. This can be a class, interface, module, function, or enum. When imported, the exported symbol is consumed directly and is not qualified by any name.
we can later consume it like this:
import App = require('./test');
var sc: App.SomeClass = new App.SomeClass();
sc.getName();
Read more here:
Optional Module Loading and Other Advanced Loading Scenarios
In some cases, you may want to only load a module under some conditions. In TypeScript, we can use the pattern shown below to implement this and other advanced loading scenarios to directly invoke the module loaders without losing type safety.
The compiler detects whether each module is used in the emitted JavaScript. For modules that are only used as part of the type system, no require calls are emitted. This culling of unused references is a good performance optimization, and also allows for optional loading of those modules.
The core idea of the pattern is that the import id = require('...') statement gives us access to the types exposed by the external module. The module loader is invoked (through require) dynamically, as shown in the if blocks below. This leverages the reference-culling optimization so that the module is only loaded when needed. For this pattern to work, it's important that the symbol defined via import is only used in type positions (i.e. never in a position that would be emitted into the JavaScript).
Above answers are correct. But just in case...
Got same error in VS Code. Had to re-save/recompile file that was throwing error.
How can I accomplish that?
Your example declares a TypeScript < 1.5 internal module, which is now called a namespace. The old module App {} syntax is now equivalent to namespace App {}. As a result, the following works:
// test.ts
export namespace App {
export class SomeClass {
getName(): string {
return 'name';
}
}
}
// main.ts
import { App } from './test';
var a = new App.SomeClass();
That being said...
Try to avoid exporting namespaces and instead export modules (which were previously called external modules). If needs be you can use a namespace on import with the namespace import pattern like this:
// test.ts
export class SomeClass {
getName(): string {
return 'name';
}
}
// main.ts
import * as App from './test'; // namespace import pattern
var a = new App.SomeClass();
In addition to A. Tim's answer there are times when even that doesn't work, so you need to:
Rewrite the import string, using the intellisense. Sometimes this fixes the issue
Restart VS Code
I had this issue and I had forgotten to export the Class.
In addition to Tim's answer, this issue occurred for me when I was splitting up a refactoring a file, splitting it up into their own files.
VSCode, for some reason, indented parts of my [class] code, which caused this issue. This was hard to notice at first, but after I realised the code was indented, I formatted the code and the issue disappeared.
for example, everything after the first line of the Class definition was auto-indented during the paste.
export class MyClass extends Something<string> {
public blah: string = null;
constructor() { ... }
}
Just in case this may works for you as it did form me, i had this files
//server.ts
class Server{
...
}
exports.Server = Server
//app.ts
import {Server} from './server.ts'
And this actually raised an error but i changed server.ts to
//server.ts
export class Server{
...
}
and it worked 😎👌
Note: i am using this config
"target": "esnext",
"module": "commonjs",
I faced the same issue in a module that has no exports. I used it for side-effects only. This is what the TypeScript docs say about importing side-effects modules:
Though not recommended practice, some modules set up some global state that can be used by other modules. These modules may not have any exports, or the consumer is not interested in any of their exports. To import these modules, use:
import "./my-module.js";
In that situation, you can fix the "File is not a module" error by simply exporting an empty object:
// side-effects stuff
export default {};
I faced the same issue ("File is not a module error") for import js in vue component
import handleClientLoad from "../../../public/js/calendar.js"
I do this and solve it
// #ts-ignore
import handleClientLoad from "../../../public/js/calendar.js"
The file needs to add Component from core hence add the following import to the top
import { Component } from '#angular/core';

Categories