Rollup can't see named export from commonjs module - node.js

I have a simple AWS Lambda function (state-info.ts) which I am tree-shaking with Rollup and it is giving me the following error:
[!] Error: 'DB' is not exported by ../../forest-fire/abstracted-admin/lib/index.js
https://github.com/rollup/rollup/wiki/Troubleshooting#name-is-not-exported-by-module
src/state-info.ts (10:9)
8: import { Lambda } from "aws-sdk";
9: import { STATES } from "./models/shared";
10: import { DB } from "abstracted-admin";
^
11: import { StateInfo } from "./models/StateInfo";
12: import { IApiResponse } from "./shared/ApiRetriever";
Now it just so happens that I wrote the module abstracted-admin (currently at v0.6.5 on npm) which it is complaining about and it DOES export DB as a named export (and as the default export). But for some reason Rollup is unhappy.
I've created a video walk-through of everything for full context: video.
For those of you who don't like video, here are the key facts/files:
abstracted-admin/lib/index.d.ts:
import { DB } from "./db";
export default DB;
export { DB, IFirebaseConfig, IFirebaseListener } from "./db";
abstracted-admin/lib/index.js:
"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
const db_1 = require("./db");
exports.default = db_1.DB;
var db_2 = require("./db");
exports.DB = db_2.DB;
and from within the abstracted-admin/package.json:
{
...
"files": ["lib"],
"main": "lib/index.js",
"typings": "lib/index.d.ts",
}
state-info.ts (aka, the file being rolled up):
import {
IDictionary,
AWSGatewayCallback,
IAWSGatewayRequest,
IAWSGatewayResponse
} from "common-types";
import GetStateInfo from "./vote-smart/get-state-info";
import { Lambda } from "aws-sdk";
import { STATES } from "./models/shared";
import { DB } from "abstracted-admin";
import { StateInfo } from "./models/StateInfo";
import { IApiResponse } from "./shared/ApiRetriever";
/** ... */
Meanwhile my rollup config is:
import cjs from "rollup-plugin-commonjs";
import resolve from "rollup-plugin-node-resolve";
import json from "rollup-plugin-json";
import ts from "rollup-plugin-typescript2";
import globals from "rollup-plugin-node-globals";
export default {
input: "src/state-info.ts",
output: {
file: "lib/state-info-rolled.js",
format: "cjs"
},
external: ["aws-sdk"],
plugins: [
globals(),
json(),
cjs({
include: "node_modules/**",
exclude: ["node_modules/aws-sdk/**"]
}),
ts(),
resolve({
jsnext: true,
main: true,
browser: false,
preferBuiltins: false,
extensions: [".js", ".json"]
})
]
};

I believe Rollup expects the below in this situation:
import admin from "abstracted-admin";
const { DB } = admin;
It's different from Webpack's behavior and has caught me a few times.

Related

TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".vue" when run node in ssr.js

I use Inertia, Vue3, and Vite as bundling assets. When I set up and run with SSR in nodejs the error always appears Unknown file extension ".vue". I've been googling for days but haven't solved it.
This is my ssr.js file which I run with nodemon
import { createSSRApp, h } from 'vue'
import { renderToString } from '#vue/server-renderer'
import { createInertiaApp } from '#inertiajs/inertia-vue3'
import createServer from '#inertiajs/server'
createServer.default((page) => createInertiaApp({
page,
render: renderToString,
resolve: async (name) => {
return (await import(`./Pages/${name}.vue`)).default;
},
setup({ app, props, plugin }) {
return createSSRApp({
render: () => h(app, props),
}).use(plugin)
},
}))
vite.config.js
import { defineConfig } from 'vite';
import laravel from 'laravel-vite-plugin';
// import react from '#vitejs/plugin-react';
import vue from '#vitejs/plugin-vue';
export default defineConfig({
plugins: [
laravel({
input: 'resources/js/app.js',
ssr: 'resources/js/ssr.mjs',
}),
// react(),
vue({
template: {
transformAssetUrls: {
base: null,
includeAbsolute: false,
},
},
}),
],
ssr: {
noExternal: ['#inertiajs/server'],
},
resolve: {
alias: {
'#': '/resources/js'
}
}
});
when I run it with nodemon ssr.js an error like this will appear
TypeError [ERR_UNKNOWN_FILE_EXTENSION]: Unknown file extension ".vue" for C:\Users\USER\Documents\biviplant\biviplant\resources\js\Pages\Test.vue

NestJS serving JSON and adds a "default" section repeating the JSON

I have a strange behaviour on an endpoint in NestJS serving a piece of JSON.
The JS with the JSON object is exporting
module.exports = Object.freeze({
translation: {
TestMessage: 'Bienvenue à React et react-i18next'
}
});
The result on the Client is:
{
"translation": {
"TestMessage": "Bienvenue à React et react-i18next"
},
"default": {
"translation": {
"TestMessage": "Bienvenue à React et react-i18next"
}
}
}
The question is where is the "default" coming from?
To paint the whole picture, below the module, controller and service:
Module
import { Module } from '#nestjs/common';
import { LoggerService } from '#modules/logger';
import { I18nController } from './i18n.controller';
import { I18nService } from './i18n.service';
#Module({
controllers: [I18nController],
providers: [I18nService, LoggerService],
exports: [I18nService]
})
export class I18nModule {}
Controller
import { Controller, Get, Param } from '#nestjs/common';
import { LoggerService } from '#modules/logger';
import { I18nService } from './i18n.service';
#Controller('i18n')
export class I18nController {
constructor(private logger: LoggerService, private i18nService: I18nService) {
this.logger.setContext(I18nController.name);
}
#Get('/:lang')
async getLanguage(#Param('lang') lang: string) {
console.log(lang);
return await this.i18nService.findOneByLanguageCode(lang);
}
}
Service
import { Injectable } from '#nestjs/common';
import { access } from 'fs/promises';
import { constants as fsconstants } from 'fs';
#Injectable()
export class I18nService {
async findOneByLanguageCode(language: string): Promise<any | null> {
const languagefile = __dirname + '/../../public/languages/' + language + '.js';
await access(languagefile, fsconstants.R_OK);
return await import(languagefile);
}
}
From the Client I do a simple http://localhost:3001/i18n/fr-FR
and get the above result.
Again, where is the 'default' section coming from?
There should be esModuleInterop enabled in your tsconfig.json
https://www.typescriptlang.org/tsconfig#esModuleInterop
a default import like import moment from "moment" acts the same as const moment = require("moment").default
That's why you have default object exist.
tsconfig.json
{
"compilerOptions": {
"esModuleInterop": true, // change it to false or remove it
}
}

Jest error "SyntaxError: Need to install with `app.use` function" when using vue-i18n plugin for Vue3

I am using vue-i18n plugin for my Vue3(typescript) application. Below is my setup function in component code
Home.vue
import {useI18n} from 'vue-i18n'
setup() {
const {t} = useI18n()
return {
t
}
}
Main.ts
import { createI18n } from 'vue-i18n'
import en from './assets/translations/english.json'
import dutch from './assets/translations/dutch.json'
// internationalization configurations
const i18n = createI18n({
messages: {
en: en,
dutch: dutch
},
fallbackLocale: 'en',
locale: 'en'
})
// Create app
const app = createApp(App)
app.use(store)
app.use(router)
app.use(i18n)
app.mount('#app')
Code works and compiles fine. But jest test cases fails for the component when it's mounting
Spec file
import { mount, VueWrapper } from '#vue/test-utils'
import Home from '#/views/Home.vue'
import Threat from '#/components/Threat.vue'
// Test case for Threats Component
let wrapper: VueWrapper<any>
beforeEach(() => {
wrapper = mount(Home)
// eslint-disable-next-line #typescript-eslint/no-empty-function
jest.spyOn(console, 'warn').mockImplementation(() => { });
});
describe('Home.vue', () => {
//child component 'Home' existance check
it("Check Home component exists in Threats", () => {
expect(wrapper.findComponent(Home).exists()).toBe(true)
})
// Threat level list existance check
it("Check all 5 threat levels are listed", () => {
expect(wrapper.findAll('.threat-level .level-wrapper label')).toHaveLength(5)
})
})
Below is the error
Please help me to resolve this.
The vue-18n plugin should be installed on the wrapper during mount with the global.plugins option:
import { mount } from '#vue/test-utils'
import { createI18n } from 'vue-i18n'
import Home from '#/components/Home.vue'
describe('Home.vue', () => {
it('i18n', () => {
const i18n = createI18n({
// vue-i18n options here ...
})
const wrapper = mount(Home, {
global: {
plugins: [i18n]
}
})
expect(wrapper.vm.t).toBeTruthy()
})
})
GitHub demo
You can also define the plugin globally in the setup/init file:
import { config } from '#vue/test-utils'
import { createI18n } from 'vue-i18n'
const i18n = createI18n({
// vue-i18n options here ...
})
config.global.plugins = [i18n]
config.global.mocks.$t = (key) => key

NestJs Testing - Is REPOSITORY part of the relevant providers/imports within module

I am trying to write the unit test cases for the mono-repo based application using nestjs.
I'm facing the following error
Nest cannot export a provider/module that is not a part of the currently processed module (CacheManagerModule). Please verify whether the exported CACHE_MANAGER_REPOSITORY is available in this particular context.
Possible Solutions:
- Is CACHE_MANAGER_REPOSITORY part of the relevant providers/imports within CacheManagerModule?
Used Packages as follows
"#nestjs/testing": "6.11.7",
"jest": "25.1.0",
"ts-jest": "25.0.0"
Please refer the sample code below
apps\api\src\modules\enduse\enduse.controller.spec.ts
import { INestApplication } from "#nestjs/common";
import { Test } from "#nestjs/testing";
import { CacheManagerModule } from "#app/cache-manager";
import * as request from "supertest";
import { EnduseController } from "./enduse.controller";
import { EnduseService } from "./enduse.service";
describe("Enduse", () => {
let app: INestApplication;
beforeAll(async () => {
const module = await Test.createTestingModule({
imports: [CacheManagerModule],
controllers: [EnduseController],
providers: [EnduseService],
}).compile();
app = module.createNestApplication();
await app.init();
});
it("/ (Get Enduse)", async () => {
const response = await request(app.getHttpServer()).get("enduse/mapping?enduse=123").expect(200);
return response;
});
});
libs\cache-manager\src\cache-manager.module.ts
import { Module } from "#nestjs/common";
import { CacheManagerService } from "./cache-manager.service";
import { CacheManagerProviders } from "./cache-manager.provider";
#Module({
providers: [CacheManagerService, ...CacheManagerProviders],
exports: [CacheManagerService, ...CacheManagerProviders],
})
export class CacheManagerModule {}
libs\cache-manager\src\cache-manager.provider.ts
import { ReviewModel } from "#app/database/models";
import { CACHEMANAGERCONST } from "./cache-manager.const";
export const CacheManagerProviders = [
{
provide: CACHEMANAGERCONST.CACHE_MANAGER_REPOSITORY,
useValue: ReviewModel,
},
];
jest.config.js
moduleNameMapper: {
"^#app/cache-manager": resolve(__dirname, "./libs/cache-manager/src")
}
How to solve the issue?
Thanks.

Unable to inject winston's logger instance with NestJS

I'm using NestJS 7.0.7 and Winston 3.2.1 (with nest-winston 1.3.3).
I'm trying to integrate Winston into NestJS, but so far, I'm unable to inject a logger instance (to actually log anything) into any controller/service.
Since I would like to use Winston across the application AND during bootstrapping, I'm using the approach as the main Nest logger:
// main.ts
import { NestFactory } from "#nestjs/core";
import { WinstonModule } from "nest-winston";
import { format, transports } from "winston";
import { AppModule } from "./app.module";
async function bootstrap(): Promise<void> {
const app = await NestFactory.create(AppModule, {
logger: WinstonModule.createLogger({
exitOnError: false,
format: format.combine(format.colorize(), format.timestamp(), format.printf(msg => {
return `${msg.timestamp} [${msg.level}] - ${msg.message}`;
})),
transports: [new transports.Console({ level: "debug" })], // alert > error > warning > notice > info > debug
}),
});
app.use(helmet());
await app.listen(process.env.PORT || 3_000);
}
bootstrap().then(() => {
// ...
});
I'm not doing anything in regard to the logging in app.module.ts:
// app.module.ts
import { SomeController } from "#controller/some.controller";
import { Module } from "#nestjs/common";
import { SomeService } from "#service/some.service";
#Module({
controllers: [SomeController],
imports: [],
providers: [SomeService],
})
export class AppModule {
// ...
}
// some.controller.ts
import { Controller, Get, Inject, Param, ParseUUIDPipe, Post } from "#nestjs/common";
import { SomeService } from "#service/some.service";
import { WINSTON_MODULE_PROVIDER } from "nest-winston";
import { Logger } from "winston";
#Controller("/api/some-path")
export class SomeController {
constructor(#Inject(WINSTON_MODULE_PROVIDER) private readonly logger: Logger, private readonly service: SomeService) {
// ...
}
...
}
The application tries to start but fails at some point:
2020-04-06T18:51:08.779Z [info] - Starting Nest application...
2020-04-06T18:51:08.787Z [error] - Nest can't resolve dependencies of the SomeController (?, SomeService). Please make sure that the argument winston at index [0] is available in the AppModule context.
Potential solutions:
- If winston is a provider, is it part of the current AppModule?
- If winston is exported from a separate #Module, is that module imported within AppModule?
#Module({
imports: [ /* the Module containing winston */ ]
})
Try importing the WinstonModule in the root AppModule, as explained in the official docs: https://github.com/gremo/nest-winston:
import { Module } from '#nestjs/common';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
const logger: LoggerConfig = new LoggerConfig();
#Module({
imports: [WinstonModule.forRoot(logger.console())],
})
export class AppModule {}
It's probably a good idea to create some kind of factory/Logging-Config in order not to have to duplicate the logger options.
import winston, { format, transports } from "winston";
export class LoggerConfig {
private readonly options: winston.LoggerOptions;
constructor() {
this.options = {
exitOnError: false,
format: format.combine(format.colorize(), format.timestamp(), format.printf(msg => {
return `${msg.timestamp} [${msg.level}] - ${msg.message}`;
})),
transports: [new transports.Console({ level: "debug" })], // alert > error > warning > notice > info > debug
};
}
public console(): object {
return this.options;
}
}
Implement Winston custom logger in NestJs project
Prerequisit:
npm install --save nest-winston winston winston-daily-rotate-file
import { NestFactory } from '#nestjs/core';
import { WinstonModule } from 'nest-winston';
import * as winston from 'winston';
import * as winstonDailyRotateFile from 'winston-daily-rotate-file';
import { AppModule } from './app.module';
const transports = {
console: new winston.transports.Console({
level: 'silly',
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
winston.format.colorize({
colors: {
info: 'blue',
debug: 'yellow',
error: 'red',
},
}),
winston.format.printf((info) => {
return `${info.timestamp} [${info.level}] [${
info.context ? info.context : info.stack
}] ${info.message}`;
}),
// winston.format.align(),
),
}),
combinedFile: new winstonDailyRotateFile({
dirname: 'logs',
filename: 'combined',
extension: '.log',
level: 'info',
}),
errorFile: new winstonDailyRotateFile({
dirname: 'logs',
filename: 'error',
extension: '.log',
level: 'error',
}),
};
async function bootstrap() {
const app = await NestFactory.create(AppModule);
app.useLogger(
WinstonModule.createLogger({
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss',
}),
winston.format.errors({ stack: true }),
winston.format.splat(),
winston.format.json(),
),
transports: [
transports.console,
transports.combinedFile,
transports.errorFile,
],
}),
);
await app.listen(4000);
}
bootstrap();
NestJs Custom Logger
NestJs Winston NPM Documentation
Note Log Levels, file names, dateformat you may edit as per your requirement. Follow officials documentations with more option.

Resources