Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to dynamically load json schema to typescript with webpack

I have a json schema describing an object Person. I would like to be able to load that schema into a typescript file directly this way:

import Person from './schema/person.schema.json';

For this, I created a loader which convert the json file to a typescript interface declaration (using json-schema-to-typescript) and then pass the result to ts-loader.

My webpack is configured this way:

webpack.config.js (excerpt)

module: {
  rules: [
    {
      test: /\.ts$/,
      loader: 'ts-loader',
    },
    {
      test: /\.schema\.json$/,
      loader: 'ts-loader!jsonschema-loader',
      exclude: /(node_modules)/,
    },
  ]
},

Following this question I configured a declaration so that the json file is considered a string:

declaration.d.ts:

declare module '*.schema.json' {
  const schema: string;
  export default schema;
}

My loader changes the filename it processes on the fly, so what the ts-loader think it loads is person.schema.ts. Moreover, I have checked the result of my loader is correct. Here it is:

/**
 * This file was automatically generated by json-schema-to-typescript.
 * DO NOT MODIFY IT BY HAND. Instead, modify the source JSONSchema file,
 * and run json-schema-to-typescript to regenerate this file.
 */

export interface Person {
  firstName: string;
  lastName: string;
  /**
   * Age in years
   */
  age?: number;
  [k: string]: any;
}

However, when I build my project, Person is not recognized as an object and the compilation fails:

index.js

import Person from './schema/person.schema.json';

const person: Person = {
  lastName: 'Doe',
  firstName: 'John',
};
console.log(person);

compilation fails with:

ERROR in ./src/index.ts
(3,15): error TS2304: Cannot find name 'Person'.

Although I defines the export of .schema.json file as string in my declaration, my loader dynamically changes the filename (this.resourcePath) to a ts file so the ts-loader should see it as a standard ts file and export the Person object.

What am I doing wrong?

like image 713
Luke Skywalker Avatar asked Dec 16 '17 12:12

Luke Skywalker


1 Answers

Use appendTsSuffixTo

I think your setup will work if you use ts-loader's appendTsSuffixTo option, rather than changing the filename yourself in your custom loader. As noted in comments, you may also need to adjust the way Person is being imported or exported.

Using appendTsSuffixTo, you'd remove the this.resourcePath = this.resourcePath + '.ts'; (or similar code) from your loader. Then adjust your webpack config to something like this:

  module: {
    rules: [
      {
        test: /\.ts$/,
        loader: 'ts-loader',
        options: {
            appendTsSuffixTo: [/\.schema.json$/]
          }
      },
      {
        test: /\.schema\.json$/,
        loader: 'ts-loader!my-own-loader',
        exclude: /(node_modules)/,
      },
    ]
  },

You can also get rid of typings.d.ts using this approach.

But why?

It wasn't immediately clear to me why your approach wouldn't work; intuitively it seemed like it should be equivalent to using appendTsSuffixTo. So I debugged through ts-loader a bit to see what was going on.

If I modify the resourcePath myself, things largely look fine in ts-loader/dist/index.js, but things start going wrong in ts-loader/dist/servicesHost.js.

Specifically, I see resolveModuleName() return undefined for my example.schema.json file when I just change this.resourcePath in my loader. However, I see it resolve correctly when using appendTsSuffixTo.

I'm not a contributor to (or expert in) ts-loader, but my take on this is that when modifying this.resourcePath in the loader, your .schema.json file will indeed compile, but then fail to be resolved wherever it is imported.

Other Considerations

Getting your webpack build to run is one thing; providing a nice developer experience in an IDE is another. See this answer to a closely related question for more thoughts on this, and this Github repo for working sample code that might be helpful.

like image 125
Mike Patrick Avatar answered Sep 21 '22 02:09

Mike Patrick