How do I mix Typescript namespaces with External modules when "Don't do it" is not an option - node.js

Situation
I have a Typescript app written using namespaces. I want to move some of this logic (Google OAuth) out of the client and into a node service. I have created a nearly complete project for that here.
This new project has a node component that will make authorisation requests to google using a secret key and a client component that can be re-used by other applications that will communicate with the server. I also have a testHarness app that uses this client to test it and make sure that I can use it in a namespace based application.
I have some shared interfaces that both the client and server consume.
I want my client code to be usable in projects that use externam modules and namespaces - i.e. my existing project.
It must be possible
I have read in many places
Do not use "namespaces" in external modules.
Don't do this.
Seriously. Stop.
Such as on this answer but I am still convinced that this must be possible. The reason that I think this is that in my project I have a dependency on RxJs. This project in my node_modules folder is used by both the client and the server.
What I have tried
In my project I have a contracts.d.ts file that I want to share between both the client and the server.
StackOverflow
I looked at this question:
Typescript es6 import module "File is not a module error"
And made my contracts look like this:
// test.js - exporting es6
export module App {
export class SomeClass {
getName(): string {
return 'name';
}
}
export class OtherClass {
getName(): string {
return 'name';
}
}
}
and then tried the various import methods listed:
import * as app1 from "./test";
import app2 = require("./test");
import {App} from "./test";
I got each of these to work so that my server AND client compiled correctly but then as soon as I tried to compile my testHarness app - that uses namespaces - it failed:
src/testHarness/testHarness.ts(4,38): error TS2304: Cannot find name 'PricklyThistle'.
src/testHarness/testHarness.ts(4,38): error TS2503: Cannot find namespace 'PricklyThistle'.
Copying Declaration Files from node_modules
As I said, I am convinced that this is possible as dependencies that I import into node_modules are used by both my client and server projects and my client can still be used with internal namespaces.
To go down this route I edited a d.ts file in the Rx ts folder and added a new interface. I verified that both client and server could use this interface and that my testHarness app (with namespaces) woudl compile. All was good!
I then copied this edited file into my common folder. I had to rename the module to avoid conflicts but then I get:
src/node/youTubeAuthenticationServer.ts(3,23): error TS2306: File '/src/common/rx.test.d.ts' is not a module.
My edited declaration file looks like:
declare module RxTest {
export interface TestInterface{
propertyOne: string;
propertyTwo: number;
}
}
declare module "rx.test" { export = RxTest; }
Workaround
For now I have just copied the interfaces that are used by both applications. This works and there are only 2 small interfaces so it's not a big deal. It is very anooying though. One of the great things about node is that is uses the same language as the browser. If you can't share code that is not good. I also have other much larger code bases that I want to do similar things with and in these situations copying code will not be a viable solution.
Any new projects I work on I will exlusively use external modules but when working with legacy code this is not always possible.
I really hope someone can help.
Thanks

If find your question a bit confusing, there's too much description and too little examples (code/configuration) of what you have.
Try to do this:
declare module "rx.test" {
export interface TestInterface{
propertyOne: string;
propertyTwo: number;
}
}
If that doesn't work, please edit your question and add the directory structure that you have, the tsconfig.json file(s) that you have, explain how you build (tsc, gulp, etc) and so on.

Related

Detect whether an npm package can run on browser, node or both

I'm building a NextJs application which uses a set of abstractions to enable code from both react (Component logic etc.) and node (API logic) to utilize same types and classes. The motive here is to make the development process seem seamless across client side and server side.
For example. a call on User.create method of the User class will behave differently based on the runtime environment (ie. browser or node) - on the browser, it will call an api with a POST request and on server it will persist data to a database.
So far this pattern worked just fine, and I like how it all turned out in terms of the code structure. However, for this to work, I have to import modules responsible for working on the server and browser to a single module (which in this case is the User class) this leads to a critical error when trying to resolve dependencies in each module.
For example I'm using firebase-admin in the server to persist data to the Firebase Firestore. But it uses fs which cannot be resolved when the same code runs on the browser.
As a work around I set the resolve alias of firebase-admin to false inside the webpack configuration (given it runs on browser) see code below.
/** next.config.js **/
webpack: (config, { isServer }) => {
if (!isServer) {
// set alias of node specific modules to false
// eg: service dependencies
config.resolve.alias = {
...config.resolve.alias,
'firebase-admin': false,
}
} else {
// set alias of browser only modules to false.
config.resolve.alias = {
...config.resolve.alias,
}
}
While this does the trick, it won't be much long until the process gets really tedious to include all such dependencies within resolve aliases.
So, my approach to this is to write a script that runs prior to npm run dev (or manually) that will read all dependencies in package.json and SOMEHOW identify packages that will not run on a specific runtime environment and add them to the webpack config. In order to this, there should be a way to identify this nature of each dependency which I don't think is something that comes right out of the box from npm or the package itself.
Any suggestion on how this can be achieved is really appreciated. Thanks`

How to use an angular module that does not support Universal with an angular/cli app using SSR

Hey everyone I have been building an Angular app that is using Universal with SSR for a while not and every so often I would include a module that would cause the server to fail silently and never knew why, last night I figured out it was because the module I tried to include (ngx-editor) does not support Universal.
Is there a way for me to include a module such as ngx-editor that does not support Universal in my application? Or do I have to find one that supports Universal?
Thanks a lot in advance.
You could try not calling this module's components depending on the platform, e.g. by checking the platform dynamically in your code ( https://angular.io/api/common/isPlatformBrowser)
import {isPlatformBrowser} from "#angular/common";
//...
constructor(#Inject(PLATFORM_ID) private platformId: Object)
{
if(isPlatformBrowser(this.platformId))
{
//call module's methods/components...
}
else { /*server side*/ }
}
You may also need to modify your server module (app.server.module.ts) to not include modules that do not support angular-universal

How to Structure a TypeScript Project like in Java with Modules and Dependencies?

i have a problem with TypeScript and how to separate modules and import them.
In Java with Maven or Gradle, i can have three modules or projects where:
project-dao: Data Access Object classes
project-dto: Data Transfer Object classes
project-services: Services classes
project-dto is transversal to dao and services, so i can include this module like a project dependency easily in both.
In TypeScript i don't know how do make the same. I see examples with imports of compiled code in Javascript with relative paths or using npm publish for later install in another project.
Someone can i help me with some approach for this issue.
Thanks.
#Paolo : I hope your question is about creating different modules and importing them to your root module. If that's the case, you can very well create a new module with #NgModule annotation. if you want to load this module with the root module, just import this in you app.module.ts as
import {customModule} from './custom.module';
If you just want to load it lazily, include this your routing, with loadChildren property.
But, if a class that's already declared in another module, whether an app module, #NgModule, or third-party module, do not add this to the declaration.
From a DTO's perspective, I guess, you need to create an incline object as
var userObject = {
email : req.body.email,
password: req.body.password
}.
UserObject can then be passed to the next layer.

Setting release configuration for SPA / webpack

I'm developing a Single Page Application and using Webpack for bundling up the modules.
One of my source files (I'm using TypeScript) has settings and configuration used in the application e.g.
// app-settings.ts
export class AppSettings {
public static ApiUrlPrefix: string = "//localhost/myapi/";
}
Which I then use in my code like:
//some-class.ts
import {AppSettings} from "./app-settings"
export class SomeClass {
contructor() {
var something = AppSettings.ApiUrlPrefix;
}
}
When release time comes, I'm going to want the settings to match the live environment.
What's a good way with either gulp, npm or webpack configs to update the settings file? I've seen the HtmlWebpackPlugin which can take a template and plug in some options, so I guess I'm looking for something similar.
You can also use the webpack.DefinePlugin. The Define plugin allows you to pass "Free Global" or "macro-like" variables into your project that you can use. To use webpack.DefinePlugin simply require() webpack into your project.
Here's the documentation and an example on how to use it.

Sharing TypeScript classes between client and server

I have a Node.js project written in TypeScript. In my project, I have a folder named "public" which contains the client side code & HTML and also a file named classes.ts which is supposed to be shared to the server side.
The problem is that I need to add "export" before the classes declaration in order to make them accessible in the server, but then in the browser I get this Error:
Uncaught ReferenceError: exports is not defined
I found these questions:
https://github.com/Microsoft/TypeScript/issues/5094,
Setup a Typescript project with classes shared between client and server apps?,
Share module between client and server with TypeScript,
which suggests using commonjs in the server but amd in the client. The problem with this solution is that they have 3 different projects (server, client and shared) whereas I only have one project in which I use commonjs.
Another suggestion is:
the other option, which is more convoluted and will require a post
build step to massage the code; if you can not use module loaders in
your client code, is to isolate all module dependencies in your server
code, then in the shared, they are just classes. Build the shared
files without --module, and no exports or imports, but all inside a
single namespace, say namespace MyApp { ... }; in your client code,
you include them directly, and emit using --out. in your server code,
you first emit the shared code to a single file, shared.js, and a
single .d.ts shared.d.ts, augment these with some code to export them
as a module, e.g. append exports = MyApp at the end of your shared.js
and shared.d.ts, then import them from your server code.
But I don't want to deal with updating .d.ts files all the time, and I'm also not sure it will work in one project.
Any suggestion how to make a TypeScript class accessible both in browser and server?
Any help will be profoundly appreciated!
This is absolutely possible.
I have a project containing both SPA client application that runs in browser and server running in node.js that both share common typescript classes. For all of this I have just one tsconfig.json file (I am still not sure that this is the best approach but for now it works just fine)
Here are parts of my setup:
Use modules (previously called external modules). No need for namespaces and d.ts files for your own modules.
module = "commonjs" in tsconfig.
On client side use System.js as module loader (this will solve your 'Uncaught ReferenceError: exports is not defined'). You can use angular2 5 min quickstart as reference how to setup system.js.
It works like a charm.

Resources