I am trying to add server-side rendering to my angular 4 (4.0.3) app using the universal, already integrated to platform-server.
I used this project as an example:
https://github.com/FrozenPandaz/ng-universal-demo
and also I was following this guide:
https://www.softwarearchitekt.at/post/2017/03/07/server-side-rendering-with-angular-4.aspx
My app.server.module, which is in app/server folder:
import {AppModule} from "../app.module";
import {ServerModule} from "#angular/platform-server";
import {NgModule} from "#angular/core";
import {AppComponent} from "../app.component";
import {BrowserModule} from "#angular/platform-browser";
import {APP_BASE_HREF} from '#angular/common';
import {routing} from "../app.routes";
#NgModule({
bootstrap: [AppComponent],
imports: [
BrowserModule.withServerTransition({
appId: 'kassir'
}),
ServerModule,
AppModule,
routing
],
providers: [
{provide: APP_BASE_HREF, useValue: "/site/"}
]
}
)
export class ServerAppModule {
}
tsconfig.server.json:
{
"compilerOptions": {
"target": "es5",
"module": "es2015",
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"experimentalDecorators": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
"noEmit": true,
"noEmitHelpers": true,
"importHelpers": true,
"strictNullChecks": false,
"lib": [
"es2015",
"dom"
],
"typeRoots": [
"node_modules/#types"
],
"types": [
"hammerjs",
"node"
]
},
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"angularCompilerOptions": {
"entryModule": "./app/server/app.server.module#ServerAppModule"
},
"exclude": [
"node_modules",
"dist",
"src/**/*.spec.ts",
"src/**/*.e2e.ts",
"../src/main.browser.aot.ts"
],
"awesomeTypescriptLoaderOptions": {
"forkChecker": true,
"useWebpackText": true
},
"compileOnSave": false,
"buildOnSave": false,
"atom": {
"rewriteTsconfig": false
}
}
webpack config for server, which is merged then to the common config:
const ngtools = require('#ngtools/webpack');
const path = require('path');
const ngcWebpack = require('ngc-webpack');
const helpers = require('./helpers');
module.exports = function(env){
return {
entry:{
"main": helpers.root('./src/main.server.ts')
},
output: {
path: path.join(process.cwd(), "dist/server"),
filename: "[name].server.bundle.js",
//chunkFilename: "[id].server.chunk.js"
},
node: {
fs: 'empty'
},
plugins: [
new ngtools.AotPlugin({
"entryModule": helpers.root('/src/app/server/app.server.module.ts#ServerAppModule'),
"exclude": [],
"tsConfigPath": helpers.root("./tsconfig.server.json"),
"skipCodeGeneration": true
}),
],
target: 'node',
module: {
rules: [
{
test: /\.ts$/,
loaders: ['#ngtools/webpack', 'angular2-template-loader']
},
]
}
}
};
Everything builds ok, main.server.bundle.js gets generated but when i start it
nodemon dist/server/main.server.bundle.js
i get a clean exit even with an empty server:
import * as express from 'express';
var server = express();
server.listen(8080, function (err) {
if (err) {
console.log(err);
} else {
console.log("server started at port 8080");
}
});
It supposed to fire either success log message or error but I can't see none of them. I also tried to launch it with node command with no success.
Would appreciate any help regarding this issue.
P.S. I've seen plenty of examples with universal, all them are using aot for server-side and then import in server.ts like that:
import { AppServerModuleNgFactory } from './aot/src/app/app.server.module.ngfactory';
Is there any way to not generate it?
Related
I am currently create a express-typescript boilerplate for myself and this is what i didnt expect because i build this boilerplate base on another boilerplate is express-typescript-starter
> Error: Cannot find module '#utils'
> Require stack:
> C:\Users\xflas\OneDrive\Documents\Personal Projects\Boilerplate\my-boilerplate\dist\app.js
> C:\Users\xflas\OneDrive\Documents\Personal Projects\Boilerplate\my-boilerplate\dist\server.js
And this is my tsconfig.json
{
"compileOnSave": false,
"compilerOptions": {
"target": "es2017",
"lib": [
"es2017",
"esnext.asynciterable"
],
"typeRoots": [
"node_modules/#types"
],
"allowSyntheticDefaultImports": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"forceConsistentCasingInFileNames": true,
"moduleResolution": "node",
"module": "commonjs",
"pretty": true,
"sourceMap": true,
"declaration": true,
"outDir": "dist",
"allowJs": true,
"noEmit": false,
"esModuleInterop": true,
"resolveJsonModule": true,
"importHelpers": true,
"baseUrl": "src",
"paths": {
"#/*": [
"*"
],
"#config": [
"config"
],
"#controllers": [
"controllers"
],
"#dtos": [
"models/dtos"
],
"#exceptions": [
"exceptions"
],
"#interfaces": [
"interfaces"
],
"#middlewares": [
"middlewares"
],
"#models": [
"models"
],
"#routes": [
"routes"
],
"#services": [
"services"
],
"#utils": [
"utils"
]
}
},
"include": [
"src/**/*.ts",
"src/**/*.json",
".env"
],
"exclude": [
"node_modules",
"src/http",
"src/logs",
"src/tests"
]
}
My app.ts
import express from 'express';
import hpp from 'hpp';
import helmet from 'helmet';
import morgan from 'morgan';
import cors from 'cors';
import swaggerJSDoc from 'swagger-jsdoc';
import swaggerUi from 'swagger-ui-express';
import cookieParser from 'cookie-parser';
import { IRoute } from '#interfaces';
import { logger, stream } from '#utils';
import { errorMiddleware } from '#middlewares';
import { Database } from '#models';
import config from '#config';
export default class App {
public app: express.Application;
public env: string;
public port: string | number;
constructor(routes: IRoute[]) {
this.app = express();
this.env = config.NODE_ENV || 'development';
this.port = config.SERVER.PORT || 8080;
this.connectToDatabase();
this.initializeMiddlewares();
this.initializeRoutes(routes);
this.initializeSwagger();
this.initializeErrorHandling();
}
private connectToDatabase() {
Database.sequelize.sync({ force: false });
}
public listen() {
this.app.listen(this.port, () => {
logger.info(`=================================`);
logger.info(`======= ENV: ${this.env} =======`);
logger.info(`🚀 App listening on the port ${this.port}`);
logger.info(`=================================`);
});
}
public getServer() {
return this.app;
}
private initializeMiddlewares() {
this.app.use(morgan(config.LOG_FORMAT, { stream }));
this.app.use(cors({ origin: config.ORIGIN, credentials: config.CREDENTIALS }));
this.app.use(hpp());
this.app.use(helmet());
this.app.use(express.json());
this.app.use(express.urlencoded({ extended: true }));
this.app.use(cookieParser());
}
private initializeRoutes(routes: IRoute[]) {
routes.forEach(route => {
this.app.use('/', route.router);
});
}
private initializeSwagger() {
const options = {
definition: {
openapi: '3.0.0',
info: {
title: 'Express API for Boilerplate',
version: '1.0.0',
description: 'Express Boilerplate API Docs',
},
},
apis: ['swagger.yaml'],
};
const specs = swaggerJSDoc(options);
this.app.use('/api-docs', swaggerUi.serve, swaggerUi.setup(specs));
}
private initializeErrorHandling() {
this.app.use(errorMiddleware);
}
}
Thank you so much
I tried to delete the node_modules and try to edit the tsconfig.json with types: ["node"] but it not working
The AWS compilation errors from within node_modules are occurring the build
ERROR in ../../../node_modules/#aws-sdk/client-dynamodb/models/models_0.ts 6321:51-59
TS2339: Property '$unknown' does not exist on type 'never'.
6319 | if (obj.NULL !== undefined) return { NULL: obj.NULL };
6320 | if (obj.BOOL !== undefined) return { BOOL: obj.BOOL };
> 6321 | if (obj.$unknown !== undefined) return { [obj.$unknown[0]]: "UNKNOWN" };
| ^^^^^^^^
6322 | };
6323 | }
6324 |
webpack compiled with 9 errors in 6844 ms
I have just updated AWS
"#aws-sdk/client-dynamodb": "^3.51.0",
"#aws-sdk/lib-dynamodb": "^3.51.0",
"#aws-sdk/util-dynamodb": "^3.51.0",
With webpack
const path = require('path');
const slsw = require('serverless-webpack');
const nodeExternals = require('webpack-node-externals');
const TsconfigPathsPlugin = require('tsconfig-paths-webpack-plugin');
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
/*
This line is only required if you are specifying `TS_NODE_PROJECT` for whatever
reason.
*/
// delete process.env.TS_NODE_PROJECT;
module.exports = {
context: __dirname,
mode: slsw.lib.webpack.isLocal ? 'development' : 'production',
entry: slsw.lib.entries,
devtool: slsw.lib.webpack.isLocal ? 'eval-cheap-module-source-map' : 'source-map',
resolve: {
extensions: ['.mjs', '.json', '.ts'],
symlinks: false,
cacheWithContext: false,
plugins: [
new TsconfigPathsPlugin({
configFile: './tsconfig.paths.json',
}),
],
},
output: {
libraryTarget: 'commonjs',
path: path.join(__dirname, '.webpack'),
filename: '[name].js',
},
target: 'node',
externals: [nodeExternals()],
module: {
rules: [
{
// Include ts, tsx, js, and jsx files.
test: /\.(ts|js)x?$/,
exclude: [
path.resolve(__dirname, 'node_modules'),
path.resolve(__dirname, '.serverless'),
path.resolve(__dirname, '.webpack'),
],
use: [
{
loader: 'ts-loader',
options: {
transpileOnly: true,
}
},
'babel-loader'
]
}
]
},
plugins: [new ForkTsCheckerWebpackPlugin()],
};
And ts-config
{
"extends": "./tsconfig.paths.json",
"compilerOptions": {
"lib": ["ESNext"],
"moduleResolution": "node",
"esModuleInterop": true,
"noUnusedParameters": false,
"noUnusedLocals": false,
"resolveJsonModule": true,
"removeComments": true,
"sourceMap": true,
"target": "ES2020",
"outDir": "lib",
"paths": {
"#functions/*": ["src/functions/*"],
"#libs/*": ["src/libs/*"],
"#api/*": ["src/api/*"],
"~/*": [
"./*"
]
},
"types": [
"#types/node",
"#types/jest"
]
},
"include": ["src/**/*.ts", "serverless.ts"],
"exclude": [
"util/**/*",
"node_modules/**/*",
".serverless/**/*",
".webpack/**/*",
"_warmup/**/*",
".vscode/**/*" ],
"ts-node": {
"require": ["tsconfig-paths/register"]
}
}
Webpack looks to be still compiling node_modules regardless of it being in the excluded list.
Stop typescript type lookup traversing parent nodes
Incompatible types found by going up directories
Alter tsconfig.json to include typeRoots as a parameter as per Prevent require(...) from looking up modules in the parent directory
{
"extends": "./tsconfig.paths.json",
"compilerOptions": {
"lib": ["ESNext"],
"allowSyntheticDefaultImports": true,
"moduleResolution": "node",
"module": "commonjs",
"esModuleInterop": true,
"noUnusedParameters": false,
"noUnusedLocals": false,
"resolveJsonModule": true,
"removeComments": true,
"sourceMap": true,
"target": "ES2020",
"outDir": "lib",
"paths": {
"#functions/*": ["src/functions/*"],
"#libs/*": ["src/libs/*"],
"#api/*": ["src/api/*"],
"~/*": [
"./*"
]
},
"typeRoots": [
"./"
],
"types": [
"#types/node",
"#types/jest"
]
},
"include": ["src/**/*.ts", "serverless.ts"],
"exclude": [
"util/**/*",
"node_modules",
"node_modules/**/*",
".serverless/**/*",
".webpack/**/*",
"_warmup/**/*",
".vscode/**/*"
],
"ts-node": {
"require": ["tsconfig-paths/register"]
}
}
I know this has been asked before but I cannot get any accepted answer to work.
My objective is to get each of my entry points to bundle as a single [entry].js for portability. Ideally I would like to have [entry].js and [entry].[modules/vendor].js but not necessary.
What I have tried:
Using webpack-node-externals with allowlist. Results in deep modules not being found
Manual externals options path: "commonjs path". Does not change result
const {} = require('path') instead of import {} from 'path'. Also doesn't change anything
The configs below which I believe gets the closest result which is just that it cannot find built-ins.
tsconfig.json
{
"compilerOptions": {
"module": "commonjs",
"moduleResolution": "node",
"esModuleInterop": true,
"outDir": "launch",
"noImplicitAny": false,
"target": "es5",
"allowJs": true,
"experimentalDecorators": true,
"emitDecoratorMetadata": true,
"allowSyntheticDefaultImports": true,
"sourceMap": true,
},
"exclude": [
"__tests__",
"dist",
"launch"
],
"include": [
"src/Auth",
"src/Gate",
"src/Patch",
"src/Account",
"src/Proxy",
]
}
webpack.config.js
const path = require('path');
module.exports = {
target: 'node',
mode: 'production',
entry: {
'gate': './src/Gate/index.ts',
'patch': './src/Patch/index.ts',
'auth': './src/Auth/index.ts',
'account': './src/Account/index.ts',
'proxy': './src/Proxy/index.ts',
},
stats: {warnings:false},
module: {
rules: [
{
test: /\.tsx?$/,
use: 'ts-loader',
exclude: /node_modules/
},
]
},
resolve: {
extensions: ['.tsx', '.ts', '.js']
},
optimization: {
usedExports: true,
chunkIds: 'natural',
splitChunks: {
// default splitChunks config
chunks: 'async',
minSize: 20000,
minChunks: 1,
maxAsyncRequests: 30,
maxInitialRequests: 30,
enforceSizeThreshold: 50000,
cacheGroups: {
defaultVendors: {
test: /[\\/]node_modules[\\/]/,
priority: -10,
reuseExistingChunk: true,
},
default: {
minChunks: 2,
priority: -20,
reuseExistingChunk: true,
},
},
}
}
};
I found the problem, it was the package cli that has its own type of managed module resolution that breaks with webpack. I have replaced it with commander.
when trying to run the lambda function I get the error "out/get-user-messages.handler is undefined or not exported". downloading the zip I see the "out" folder in the root and get-user-messages.js inside though. and I am exporting the handler
get-user-messages.ts
import "reflect-metadata";
import { APIGatewayEvent, APIGatewayEventRequestContext, APIGatewayProxyEvent } from "aws-lambda";
export const handler = async (event: APIGatewayProxyEvent, context: APIGatewayEventRequestContext) => {
/** Code **/
}
tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"module": "es2015",
"target": "esnext",
"noImplicitAny": false,
"outDir": "./out",
"strict": false,
"resolveJsonModule": true,
"strictNullChecks": false,
"moduleResolution": "node",
"emitDecoratorMetadata": true,
"esModuleInterop": true,
},
"include": ["src/**/*"],
"exclude": ["node_modules", "**/*.spec.ts", "**/*.test.ts"],
"paths": {
"#/*": ["src/*"]
}
}
webpack.config.js
const ForkTsCheckerWebpackPlugin = require('fork-ts-checker-webpack-plugin');
var path = require('path');
/** #type {import('webpack').Configuration} */
module.exports = {
mode: "production",
module: {
mode: "production",
rules: [
{
test: /\.js|.ts|.tsx/,
exclude: /node_modules/,
use: 'babel-loader'
},
],
},
resolve: {
extensions: ['*', '.js', '.jsx', '.tsx', '.ts', '.json'],
},
module: {
rules: [
{
test: /\.(ts|js)x?$/,
exclude: /node_modules/,
use: ["babel-loader"]
}
]
},
plugins: [new ForkTsCheckerWebpackPlugin()],
externals: ['aws-sdk'], //avoid un-needed modules since aws-sdk exists in aws lambdas already
entry: {
//list of compiled files
"get-user-messages": path.join(__dirname, "./src/lambda/get-user-messages.ts"),
"insert-user-message": path.join(__dirname, "./src/lambda/insert-user-message.ts"),
"mark-read-user-message": path.join(__dirname, "./src/lambda/mark-read-user-message.ts"),
"get-application-release-notes": path.join(__dirname, "./src/lambda/get-application-release-notes.ts"),
"insert-application-release-note": path.join(__dirname, "./src/lambda/insert-application-release-note.ts")
},
target: 'node',
output: {
path: path.join(__dirname, "./out"),
filename: "[name].js",
library: "[name]",
libraryTarget: 'commonjs'
}
};
.babelrc
{
"presets": [
["#babel/preset-env",{"targets":{"node":"14"}, "modules":false}],
"#babel/preset-typescript"
],
"sourceType": "unambiguous",
"plugins": [
"babel-plugin-transform-typescript-metadata",
["#babel/plugin-proposal-decorators", { "legacy": true }],
"#babel/plugin-proposal-class-properties",
"#babel/plugin-transform-runtime"
]
}
the issue was the "library" option in the output section of webpack.config.js, removing that allowed me to successfully run the lambda function
Regarding to the webpack docs for external libraries and nodejs, there is the information that one should be able to import node modules. This works perfectly unless I want to import http2. It prints the following error:
ERROR in ./src/index.ts
Module not found: Error: Can't resolve 'http2' in 'absolutepath/to/src'
The entry typescript file simplified looks like this:
import { readFileSync } from 'fs';
import { createSecureServer } from 'http2';
const server = createSecureServer({...});
And the webpack configuration file looks like follows:
const path = require('path');
module.exports = {
entry: './src/index.ts',
output: {
path: path.resolve(__dirname, 'dist'),
filename: 'index.js'
},
node: {
global: true,
process: true,
fs: 'empty',
http2: 'empty'
},
module: {
rules: [
{ test: /\.ts$/, use: 'ts-loader' }
]
},
resolve: {
extensions: [".ts", ".tsx", ".js"],
modules: [path.resolve(__dirname, '/src'), 'node_modules/'],
descriptionFiles: ['package.json']
},
mode: 'production'
}
And the tsconfig.json
{
"compileOnSave": false,
"compilerOptions": {
"experimentalDecorators": true,
"target": "es5",
"module": "commonjs",
"emitDecoratorMetadata": true,
"outDir": "dist",
"declaration": false,
"sourceMap": true,
"removeComments": true,
"moduleResolution": "node",
"strict": true,
"noImplicitAny": false,
"typeRoots": [
"node_modules/#types"
],
"lib": [
"es2017"
]
}
}
E.g. importing fs works without errors. I also have seen the node-libs-browser plugin where the http2 module is not listed. But I don't want to use http2 within the browser but just for bundling my backend application. Any suggestions or ideas?
Thanks and cheers!