Can not import my custom typescript npm package - node.js

I make a typescript npm package, simply like this:
Core.ts
class Core{
constructor(){}
//some functions
}
export = Core;
Core.d.ts
declare class Core {
constructor(){}
//some functions
}
export = Core;
Package.json
{
"name": "#company/Core",
"main": "dist/Core.js",
"typings": "dist/Core" //.d.ts
//etc
}
I make this module with webpack and ts-loader.
In my project, index.ts
import Core = require('#company/Core');
let core = new Core(); //Error, Core is not a function
I have tried to change export and import in some form but no luck. Do I miss some knowlage?

Finally, I solve my promble by myself. I add two properties in webpack configuration file, then recompile it and works well.
webpack.config.js
module.exports = {
output: {
libraryTarget: 'commonjs'
}
//etc
}

Related

The injectable 'PlatformLocation' needs to be compiled using the JIT compiler, but '#angular/compiler' is not available

My Angular application is served via Node 16.13.0. After updating to Angular 13, I'm receiving the following error:
JIT compilation failed for injectable [class PlatformLocation]
file:///Users/btaylor/work/angular-apps/dz-outages-ui/node_modules/#angular/core/fesm2015/core.mjs:4058
throw new Error(message);
^
Error: The injectable 'PlatformLocation' needs to be compiled using the JIT compiler, but '#angular/compiler' is not available.
The injectable is part of a library that has been partially compiled.
However, the Angular Linker has not processed the library such that JIT compilation is used as fallback.
Ideally, the library is processed using the Angular Linker to become fully AOT compiled.
Alternatively, the JIT compiler should be loaded by bootstrapping using '#angular/platform-browser-dynamic' or '#angular/platform-server',
or manually provide the compiler with 'import "#angular/compiler";' before bootstrapping.
at getCompilerFacade (file:///Users/btaylor/work/angular-apps/dz-outages-ui/node_modules/#angular/core/fesm2015/core.mjs:4058:15)
at Module.ɵɵngDeclareFactory (file:///Users/btaylor/work/angular-apps/dz-outages-ui/node_modules/#angular/core/fesm2015/core.mjs:32999:22)
at file:///Users/btaylor/work/angular-apps/dz-outages-ui/node_modules/#angular/common/fesm2015/common.mjs:90:28
at ModuleJob.run (node:internal/modules/esm/module_job:185:25)
at async Promise.all (index 0)
at async ESMLoader.import (node:internal/modules/esm/loader:281:24)
at async loadESM (node:internal/process/esm_loader:88:5)
at async handleMainPromise (node:internal/modules/run_main:65:12)
I have tried numerous solutions, such as: Angular JIT compilation failed: '#angular/compiler' not loaded
Currently, I have "type": "module" in my package.json
I have updated my postinstall command to: ngcc --properties es2020 browser module main --first-only --create-ivy-entry-points
I also added import '#angular/compiler'; to my main.ts file.
The project will compile, but won't run via Node.
I believe I have found the solution (presuming you are using jest as your test runner). In the test-setup.ts file my project still was using the outdated import for jest-preset-angular. Instead of import 'jest-preset-angular'; try using import 'jest-preset-angular/setup-jest';.
This addressed the issue for me.
It seems angular 13 made babel-loader a mandatory requirement to link Ivy-native packages. https://github.com/angular/angular/issues/44026#issuecomment-974137408 - and the issue is happening as core Angular libraries are already compiled with the Ivy package format.
I haven't yet made the change in my own projects, but processing .mjs files with babel and a special linker should be enough.
import { dynamicImport } from 'tsimportlib';
/**
* Webpack configuration
*
* See: http://webpack.github.io/docs/configuration.html#cli
*/
export default async (options: IWebpackOptions) => {
const linkerPlugin = await dynamicImport('#angular/compiler-cli/linker/babel', module);
const config: any = {
module: {
rules: [{
test: /\.mjs$/,
loader: 'babel-loader',
options: {
compact: false,
plugins: [linkerPlugin.default],
},
resolve: {
fullySpecified: false
}
}
}
}
}
I'll make more updated to this post once I am able to test it myself.
If I correctly understand, you use Angular Universal also.
I want to share my experience with the same problem which came to my project after updating Angular from v8 to v13.
First of all, I want to say that the main problem was with the incorrect started file for SSR. In my project, it was server.js and now it's main.js.
So, maybe your project also tries to start from the incorrect file which doesn't have the necessary code to start it.
More details:
I updated the project step by step, increasing the version as recommended by the Angular team and according to https://update.angular.io/?l=3&v=8.2-13.0
Unfortunately, at every step, I only checked the version of SPA without SSR and only compiled the project to check that all is OK, but didn't start it with SSR. Now I can't say when the problem with the JIT compiler started.
And when I finished updating and fixing bugs with SPA, compiled the project with SSR, and tried to start it I saw this problem:
Error: The injectable 'PlatformLocation' needs to be compiled using the JIT compiler, but '#angular/compiler' is not available.
I tried different solutions like you, but nothing helped. After that, I created a new clean project with ng13 and added Angular Universal. And compare my project with new, generated by Angular-CLI.
What I changed in my project(sorry, I can't show the project - NDA):
angular.json
"projects": {
"projectName": {
...
"architect": {
...
"server": {
...
"options": {
"outputPath": "dist/server",
//"main": "src/main.server.ts", //removed
"main": "server.ts", //added
...
}
}
}
}
}
package.json
"scripts": {
...
//"serve:production:ssr": "node dist/server --production" // removed. It was incorrect file server.js which gave the error from this case
"serve:production:ssr": "node dist/server/main --production", // added
...
"postinstall": "ngcc --properties es5 browser module main --first-only" // added. In my case, actual version is es5
}
3.server.ts
import 'zone.js/node';
import { ngExpressEngine } from '#nguniversal/express-engine';
import * as express from 'express';
import { join } from 'path';
import { AppServerModule } from './src/main.server';
...
// after all imports I inserted previous code into function run()
...
export function run() {
// previous code from project with some small changes
}
//and added next code from clean project:
// Webpack will replace 'require' with '__webpack_require__'
// '__non_webpack_require__' is a proxy to Node 'require'
// The below code is to ensure that the server is run only when not requiring the bundle.
declare const __non_webpack_require__: NodeRequire;
const mainModule = __non_webpack_require__.main;
const moduleFilename = mainModule && mainModule.filename || '';
if (moduleFilename === __filename || moduleFilename.includes('iisnode')) {
run();
}
export * from '../src/main.server';
src/main.server.ts
// was only:
export { AppServerModule } from './app/app.server.module';
// replaced by code from clean project:
/***************************************************************************************************
* Initialize the server environment - for example, adding DOM built-in types to the global scope.
*
* NOTE:
* This import must come before any imports (direct or transitive) that rely on DOM built-ins being
* available, such as `#angular/elements`.
*/
import '#angular/platform-server/init';
import { enableProdMode } from '#angular/core';
import { environment } from './environments/environment';
if (environment.production) {
enableProdMode();
}
export { AppServerModule } from './app/app.server.module';
export { renderModule, renderModuleFactory } from '#angular/platform-server';
src/main.ts
...
//this part of code
document.addEventListener("DOMContentLoaded", () => {
platformBrowserDynamic()
.bootstrapModule(AppModule)
.catch(err => console.log(err));
});
// replaced by
function bootstrap() {
platformBrowserDynamic().bootstrapModule(AppModule)
.catch(err => console.error(err));
};
if (document.readyState === 'complete') {
bootstrap();
} else {
document.addEventListener('DOMContentLoaded', bootstrap);
}
//but I don't think it plays a role in solving the JIT problem. Wrote just in case
tsconfig.server.json
{
"extends": "./tsconfig.app.json",
"compilerOptions": {
"outDir": "./out-tsc/app-server",
"module": "commonjs",// it's only in my case, I don't have time for rewrote server.ts now
"types": ["node"],
},
"files": [
"src/main.server.ts",
"server.ts"
],
"include":["server/**/*.ts","node/*.ts"],
"angularCompilerOptions": {
"entryModule": "./src/app/app.server.module#AppServerModule"
}
}
Ok, I think that's all. After all these edits I don't have file /dist/server.js and start the project from /dist/server/main.js without error from this case.
N.B.: When I was updating the project step-by-step I noticed that the process of updating nguniversal by Angular-CLI didn't change anything in the project. Only the version of packages. That's why I recommend comparing your project with an actual clean project manually

Cannot redeclare exported variable ''

I have a npm module in another directory that I'm using in another project using npm link, however when I import the module and try to use the function I get the following error a bunch of errors even though the typescript compiled successfully. Here is my tsconfig for the npm module:
{
"compilerOptions": {
"target": "es5",
"module": "commonjs",
"declaration": true,
"outDir": "./lib",
"strict": true,
},
"include": ["src"],
"exclude": ["node_modules"]
}
Here is the index.ts of the module:
import * as APIServices from "./API/API"
import APIPage from "./API/APIPage"
export { APIServices, APIPage }
And here is how I'm trying to use the package:
import APIServices from 'common-backend'
console.log(APIServices)
But when I run the file I get the following errors:
TSError: ⨯ Unable to compile TypeScript:
../../common-backend/lib/index.js:3:1 - error TS2323: Cannot redeclare exported variable 'APIPage'.
3 exports.APIPage = exports.APIServices = void 0;
~~~~~~~~~~~~~~~
../../common-backend/lib/index.js:3:19 - error TS2323: Cannot redeclare exported variable 'APIServices'.
3 exports.APIPage = exports.APIServices = void 0;
~~~~~~~~~~~~~~~~~~~
../../common-backend/lib/index.js:5:1 - error TS2323: Cannot redeclare exported variable 'APIServices'.
5 exports.APIServices = APIServices;
~~~~~~~~~~~~~~~~~~~
../../common-backend/lib/index.js:7:1 - error TS2323: Cannot redeclare exported variable 'APIPage'.
7 exports.APIPage = APIPage_1.default;
Finally this is the index.js that the errors are being thrown on:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
exports.APIPage = exports.APIServices = void 0;
var APIServices = require("./API/API");
exports.APIServices = APIServices;
var APIPage_1 = require("./API/APIPage");
exports.APIPage = APIPage_1.default;
Is there something that I forgot to add in the tsconfig? Is this the fault of the compiler transpiling the typescript incorrectly? Or does it have to do something with the way I'm import and exporting the files? I've been scratching my head for a while on this one and nobody else seems to have had the same issue.
Edit:
Forgot to include the API class I'm trying to import:
import { Validators } from "./Validators"
import { APIRoute, Config } from "./helpers/Interface"
import { Router } from "express"
const router = Router()
class API {
private config: Config
private _routes: APIRoute[]
constructor(config: Config) {
this.config = config
this._routes = []
}
get routes() : APIRoute[] {
return this._routes
}
set routes(routes: APIRoute[]) {
this._routes = [...routes]
}
/**
* Add route to routes array
* #param route
*/
addRoute(route: APIRoute) : void {
this.routes.push(route)
}
/**
* Instatiates existing routes in the routes array
*/
loadRoutes() : void {
for(const route of this._routes) {
try {
new route.module(router, route, this.config.authFunction)
}
catch(error) {
console.error(route)
console.error(error)
}
}
}
}
export { API, Validators, router }
I don't think will solve all your problems but your imports seem off. There are two ways of handling imports.
export default APIServices
And then you can import like you have
import APIServices from 'common-backend'
Whereas if you want to export multiple named things like you have
export { APIServices, APIPage }
You need to import them one of two ways
import { APIServices } from 'common-backend'
Or I think you can do something like this but I am less familiar with it because I personally don't like how it looks (I much rather just import what I need)
import * as CommonBackend from 'common-backend'
// then use CommonBackend.APIServices
So it turns out the issue was that I was using ts-node to compile and run the code in the project using the package. I changed node --inspect -r ts-node/register src/app.ts" to simply node ./dist/src/app.js and that solved everything. Although not sure what underlying elements were causing the javascript not to be run properly.

How to bundle an environment dependant package?

problem
Currently my package is developed in a way where to import it you need to decide on your target. So:
import * as myLib from 'mylib/node' // if i want to use the node implementation
import * as myLib from 'mylib/web' // if i want to use the web implementation
The functionality is identical, the implementation differs tho because they use different APIs. I want to move to a single import that will work for node and web. To do that i changed my code to detect whether its running in node or the web. This alows me to import it like this:
import * as myLib from 'mylib'
Which works. However when i go to bundle some code using mylib with webpack (as web target) it goes bonkers as it tries to bundle the nodejs implementation (fails on bundling packages like worker_threads)
webpack.config.ts:
import * as path from 'path'
import { Configuration } from 'webpack'
const config: Configuration = {
entry: './dist/index.js',
target: 'web',
output: {
filename: 'mylib.web.js',
path: path.resolve(__dirname, 'dist'),
library: 'mylib',
libraryTarget: 'umd'
},
mode: 'production'
}
export default config
question
How can i bundle such a package or write my package in a way to support both node and web and bundle correctly.
edits
To specify i merged web and node implementation in such manner:
Before:
// mylib/node
export const func = () => {
// using worker_threads here
}
// mylib/web
export const func = () => {
// using web worker here
}
After:
// mylib
export const func = () => {
if(/* am i in node test */) {
// execute the worker_threads implementation
} else {
// execute the web workers implementation
}
}

How to export and use multiple custom modules in the same library with React Native

So I have a React Native custom library that I made that has 2 modules made, moduleA and moduleB. How do I export them so that I can call both of the modules in my project?
Before when I had only one module it would work perfectly like this:
//Index.js file of my library
import { NativeModules } from 'react-native';
const { moduleA } = NativeModules;
export default moduleA;
and then in my React native project I could call it like this
//Home.js file of my project
import moduleA from 'custom_library';
moduleA.method(); //Works fine
But now that I have two modules, I'm not sure how to change the modules and the import such that both can be used. Is it even possible? The end goal that I want to be able to do is
//Home.js file of my project
import { moduleA, moduleB } from 'custom_library';
moduleA.method();
moduleB.method();
Sorry for the basic question, but I'm getting frustrated that nothing I've tried is really working.
Just use the export keyword. You will find more detailed examples here.
index.js of custom_library
import { NativeModules } from 'react-native';
const { moduleA, moduleB } = NativeModules;
export { moduleA, moduleB };
home.js
import { moduleA, moduleB } from 'custom_library';
moduleA.method();
moduleB.method();

Angular 2 - Import html2canvas

I have installed html2canvas on my angular 2 project using npm install html2canvas --save. If I now go to any file and write import * as html2canvas from 'html2canvas' it gives the error:
Cannot find module 'html2canvas'
My package file looks like this:
{
...
"scripts": {
...
},
"dependencies": {
...
"html2canvas": "^0.5.0-beta4",
...
},
"devDependencies": {
...
}
}
The file on which I'm trying to import the html2canvas is:
import { Injectable } from '#angular/core';
import * as jsPDF from 'jspdf';
import * as html2canvas from 'html2canvas';
#Injectable ()
export class pdfGeneratorService {
...
}
Since Angular2 uses typescript, you need to install the typescript definition files for that module.
It can be installed from #types (if it exists). If it doesn't you can create your own definition file and include it in your project.
in angular 9 i use it this way:
import html2canvas from 'html2canvas';
....
html2canvas(this.head2print.nativeElement).then(_canvas => {
hdr = _canvas.toDataURL("image/png");
});
Also, the onrendered option for callback function may not work. Instead, you may use "then" as below:
html2canvas(document.body).then((canvas) => {
document.body.appendChild(canvas);
});
https://stackoverflow.com/a/45366038/3119507
Ran into the same issue running Angular 8. It still didn't work after installing the #types. What worked for me was to include the html2canvas library using require instead.
const html2canvas = require('../../../node_modules/html2canvas');
Then to take the screenshot:
#ViewChild('screenshotCanvas') screenCanvas: ElementRef;
html2canvas(this.screenCanvas.nativeElement).then(canvas => {
var imgData = canvas.toDataURL("image/png");
console.log("ENTER takeScreenshot: ",imgData )
document.body.appendChild(imgData);
})

Resources