Developing a TypeScript library for versatile features across multiple projects

My goal is to export multiple classes, some independent and others interdependent, encapsulated within a single namespace, in the form of a module for external project utilization.

To achieve this, I have configured a webpack build to compile these classes into one minified .js file and one .d.ts file, all enveloped by the namespace "Platform".

Below is an example of the custom events class used:


namespace Platform {
    export class GameEvent {
        ****code****
    }
}

The issue arises when I encase them in the namespace; the build fails with the following error:

ERROR in ./Utilities/GameEvent.ts Module build failed (from ../node_modules/ts-loader/index.js): Error: TypeScript emitted no output for \Platform\src\Utilities\GameEvent.ts. at makeSourceMapAndFinish (\Platform\node_modules\ts-loader\dist\index.js:53:18) at successLoader (\Platform\node_modules\ts-loader\dist\index.js:40:5) at Object.loader (\Platform\node_modules\ts-loader\dist\index.js:23:5)

Here is my tsconfig:

{
    "compilerOptions": {
        "target": "es6",
        "module": "esnext",
        "strict": true,
        "noEmit": false,
        "importHelpers": true,
        "moduleResolution": "node",
        "esModuleInterop": true,
        "sourceMap": true,
        "baseUrl": "./src",
        "rootDir": "./src",
        "outDir": "./types",
        "emitDeclarationOnly": true,
        "declaration": true,
        "skipLibCheck": true,
        "forceConsistentCasingInFileNames": true,
        "lib": [
            "es6",
            "dom"
        ],
        "removeComments": true,
        "typeRoots": [
            "node_modules/@types",
            "node_module/phaser/types"
        ],
        "types": [
            "phaser",
            "jest"
        ]
    },
    "include": [
        "src/**/*",
    ]
}

Here is my webpack.config.js:

const path = require("path");
const webpack = require('webpack');
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const DeclarationBundlerPlugin = require('declaration-bundler');
const fs = require('fs');

const srcDir = path.resolve(__dirname, 'src');
const typesDir = path.resolve(__dirname, 'types');

function scanDirectory(dir) {
  const fileArr = [];

  fs.readdirSync(dir).forEach((file) => {
    const filepath = path.join(dir, file);
    if (fs.lstatSync(filepath).isDirectory()) {
      fileArr.push(...scanDirectory(filepath));
    } else if (/\.tsx?$/.test(file)) {
      fileArr.push(path.resolve(filepath));
    }
  });

  return fileArr;
}

const entryPoints = scanDirectory(srcDir);
const typeEntryPoints = scanDirectory(typesDir);

module.exports = {
  mode: "production",
  context: path.resolve(__dirname, 'src'),
  entry: {
    'platform': entryPoints
  },
  output: {
    path: path.resolve(__dirname, 'dist'),
    filename: "[name].min.js",
  },
  externals: {
    phaser: 'phaser',
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: [
          {
            loader: 'ts-loader',
          },
        ],
        include: [path.resolve(__dirname, 'src')],
        exclude: /node_modules/,
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new webpack.DefinePlugin({
      'typeof SHADER_REQUIRE': JSON.stringify(false),
      'typeof CANVAS_RENDERER': JSON.stringify(true),
      'typeof WEBGL_RENDERER': JSON.stringify(true)
    }),
    new DeclarationBundlerPlugin({
      entry: typeEntryPoints,
      moduleName: 'Platform',
      out: './platform.d.ts',
    }),
  ],
  performance: { hints: false },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: true,
          safari10: true,
          mangle: true,
          output: {
            comments: false
          }
        }
      })
    ]
  }
};

These are my devDependencies:

"@jest/globals": "^29.3.1",
    "@declaration-bundler": "^1.0.1",
    "@types/jest": "^29.2.5",
    "before-build-webpack": "^0.2.13",
    "clean-webpack-plugin": "^3.0.0",
    "glob": "^8.0.3",
    "html-webpack-plugin": "^5.3.1",
    "jest": "^29.3.1",
    "jest-canvas-mock": "^2.4.0",
    "jest-environment-jsdom": "^29.3.1",
    "terser-webpack-plugin": "^5.3.6",
    "ts-jest": "^29.0.3",
    "ts-loader": "^8.0.18",
    "ts-node": "^10.9.1",
    "typescript": "^4.9.4",
    "webpack": "^5.28.0",
    "webpack-cli": "^4.9.1"

I attempted using "export default class" individually for each file without the namespace, but upon publishing the package and utilizing it in another project, it fails to recognize as a module and subsequently fails during build/test processes.

Looking for guidance on how to approach this effectively.

Answer №1

I have successfully resolved the issue, even though it is not exactly how I envisioned it, it is functioning smoothly.

Below is the content of my webpack.config.js file:

const path = require("path");
const webpack = require('webpack');
const { CleanWebpackPlugin } = require("clean-webpack-plugin");
const TerserPlugin = require("terser-webpack-plugin");
const DtsBundleWebpack = require("dts-bundle-webpack");
const removeEmptyDirectories = require('remove-empty-directories');

const libPath = path.resolve(__dirname, 'lib');

module.exports = {
  mode: "production",
  context: path.resolve(__dirname, 'src'),
  entry: {
    'platform': "./platform.ts"
  },
  output: {
    path: libPath,
    filename: "[name].min.js",
    libraryTarget: 'commonjs2'
  },
  module: {
    rules: [
      {
        test: /\.tsx?$/,
        use: 'ts-loader',
      },
    ],
  },
  resolve: {
    extensions: ['.tsx', '.ts', '.js'],
  },
  plugins: [
    new CleanWebpackPlugin(),
    new webpack.DefinePlugin({
      'typeof SHADER_REQUIRE': JSON.stringify(false),
      'typeof CANVAS_RENDERER': JSON.stringify(true),
      'typeof WEBGL_RENDERER': JSON.stringify(true)
    }),
    new DtsBundleWebpack({
      name: "<lib name>",
      main: "lib/platform.d.ts",
      out: "platform.d.ts",
      removeSource: true,
      outputAsModuleFolder: true
    }),
    function () {
      this.hooks.done.tap("Done", (stats) => {
        removeEmptyDirectories(libPath);
      });
    }
  ],
  performance: { hints: false },
  optimization: {
    minimize: true,
    minimizer: [
      new TerserPlugin({
        terserOptions: {
          compress: true,
          safari10: true,
          mangle: true,
          output: {
            comments: false
          }
        }
      })
    ]
  }
};

The structure of my files is as follows:

file structure src/ folder

Each index.ts file contains an "export * from 'filename'" for all the class files, while the platform.ts file exports all modules.

Answer №2

Here's another thing to watch out for: while it may not be the issue in this specific case, a frequent reason for encountering the

Error: TypeScript emitted no output for _____.ts
message is when the noEmit option is enabled in your tsconfig.json.

Similar questions

If you have not found the answer to your question or you are interested in this topic, then look at other similar questions below or use the search

Vue's span function is yielding the promise object

In my Vue component, I am using the function getOrderCount to fetch the number of orders from a specific URL and display it in one of the table columns. <div v-html="getOrderCount(user.orders_url)"></div> async getOrderCount(link) { ...

What is the best way to implement Bootstrap 5 jQuery plugins in an ES6 project with Webpack?

Currently in the process of transitioning an ES6 project from Bootstrap 4 to Bootstrap 5, encountering the following error: Error: Uncaught TypeError: bootstrapElement.Tooltip is not a function According to the Migration Notes, Bootstrap 5 no longer inc ...

Rendering a Nativescript component once the page has been fully loaded

I'm currently working on integrating the WikitudeArchitectView into my TypeScript code. I've successfully registered the element in the main.ts TypeScript file: var architectView = require("nativescript-wikitudearchitectview"); registerElement ...

Tips for monitoring/faking method invocations within an Angular 5 service's constructor

My service involves making 2 method calls in the constructor: constructor(private http: HttpClient) { this.apiURL = environment.apiURL; this.method(); this.method2().subscribe(); } I am facing difficulties testing this service in the Test ...

Angular 2 Unit test issue: Unable to resolve parameters for 'RequestOptions' class

I am currently working on testing a simple component that has some dependencies. One of the requirements is to provide certain providers for the test. /* tslint:disable:no-unused-variable */ import { By } from '@angular/platform-browser&ap ...

In TypeScript/Angular, what is the best way to share model variables between a Service class and a controller class?

Is there a way for the Controller and Service classes to access the same model without explicitly passing it? For example: Controller Class : import { SearchModel } from "../../models/SearchModel"; import { SearchService } from "../../components/SearchS ...

Require users to sign in immediately upon landing on the homepage in Next JS version 13 or 14

I have created a traditional dashboard application using Next.js 13 with a pages router that places all pages behind the /dashboard route, such as /dashboard/users, /dashboard/orders, and so on. I want to ensure that when a user visits the website, a fork ...

Navigating an array using ngFor and encountering an error message saying, "Identifier not specified"

While using ngFor to iterate through an array, I encountered the following error message: "Identifier 'expenseitem' is not defined. The component declaration, template variable declarations, and element references do not contain such a memb ...

The attribute 'XXX' is not found within the type 'IntrinsicAttributes & RefAttributes<any>'

My coding hobby currently involves working on a React website using TypeScript. I recently came across a free Material UI template and decided to integrate it into my existing codebase. The only challenge is that the template was written in JavaScript, cau ...

Whenever the route changes in Angular, the components are duplicated

Whenever I switch routes in my Angular application, such as going from home to settings and back to home, all the variables seem to be duplicated from the home page and are never destroyed. I noticed that I created a loop in the home component that displa ...

Exploring the TypeScript Type System: Challenges with Arrays Generated and Constant Assertions

I am currently grappling with a core comprehension issue regarding TypeScript, which is highlighted in the code snippet below. I am seeking clarification on why a generated array does not function as expected and if there is a potential solution to this pr ...

How can I select just one element to be impacted by a click event when using map in TypeScript?

Currently, I'm facing an issue where I want to change the icon of a button when it's selected. The problem is that using map affects all buttons even if only one is selected. // ... const [clicked, setClicked] = useState(false); <Button sta ...

The TypeScript error arises when an element implicitly contains an 'any' type due to the inability to use an expression of type 'any' to index a specific type

Encountering an Issue: Element implicitly has an 'any' type because expression of type 'any' can't be used to index type '{ foo: string; bar: string; }'.ts(7053) Within the following code snippet: const CATEGORY_COLORS ...

What steps should I take to verify the validity of an Angular form?

I am struggling with validating an inscription form in HTML. Despite trying to implement validations, the inputs are still being saved in the database even when they are empty. Here are the validations I want to include: All inputs are required Email addr ...

Is Grouping Together Installed Private Modules Possible?

Exploring a modular JavaScript approach in my upcoming project is a new concept for me. I would prefer explanations to be simple due to my limited experience. I have uploaded my private package on npm: @name/package-name The private package contains mul ...

The issue of saving Rails input from an Angular2 front end has been causing some trouble

Currently, I am in the process of developing a front-end application using Angular 2 that retrieves data from a Rails 5 API. This specific application serves as a network inventory tool. One particular feature of this app is an Asset-form in Angular2 whic ...

Is it possible to pass a Styled Components Theme as Props to a Material UI element?

After spending 9 hours scouring the internet for a solution, I am at my wit's end as nothing seems to work. Currently, I am developing a React component using TypeScript. The issue lies with a simple use of the Material UI Accordion: const Accordion ...

Is it possible to verify or authenticate the properties received directly from the associated type or interface?

Looking for a more efficient way to handle validation in my component that takes an array of tabs and children as props. I would like to check if the children provided are the same length as the tabs array directly from the type declaration or any cleaner ...

Having trouble with module.export in CompoundJS?

Hello, I am just getting started with CompoundJS and currently working on a practice project. Following the tutorial provided at this link: Using Mongoose Models Within my db/schema.js file, I have added the following code: customSchema(function () { ...

Using Typescript: How to access a variable beyond the scope of a loop

After creating an array, I need to access the elements outside of the loop. I am aware that they are not in the scope and using 'this.' before them does not grant access. colIdx = colIdx + this.columns.findIndex(c => c.editable); this.focusIn ...