I am attempting to create a webpack loader that converts a file containing a description of API data structures into a set of TypeScript interfaces.
In my concrete case,
If your API follows the swagger spec, you can use the npm package swagger-ts-generator to generate TypeScript files from it:
Swagger TypeScript code generator
Node module to generate TypeScript code for Angular (2 and above) based on Webapi meta data in Swagger v2 format.
Basically, you give it the swagger URL and it generates TypeScript. The examples are for Gulp, but they should port over to WebPack fairly well:
var swagger = {
url: 'http://petstore.swagger.io/v2/swagger.json',
//url: 'http://127.0.0.1/ZIB.WebApi.v2/swagger/docs/v1',
swaggerFile: folders.swaggerFolder + files.swaggerJson,
swaggerFolder: folders.swaggerFolder,
swaggerTSGeneratorOptions: {
modelFolder: folders.srcWebapiFolder,
enumTSFile: folders.srcWebapiFolder + 'enums.ts',
enumLanguageFiles: [
folders.srcLanguagesFolder + 'nl.json',
folders.srcLanguagesFolder + 'en.json',
],
modelModuleName: 'webapi.models',
enumModuleName: 'webapi.enums',
enumRef: './enums',
namespacePrefixesToRemove: [
],
typeNameSuffixesToRemove: [
]
}
}
First off, kudos for providing an MCVE. This is a really interesting question. The code I worked with to put this answer together is based on said MCVE, and is available here.
Missing File?
This is a most unhelpful error message indeed. The file is clearly in that location, but TypeScript will refuse to load anything that doesn't have an acceptable extension.
This error is essentially hiding the real error, which is
TS6054: File 'c:/path/to/project/example.api' has unsupported extension. The only supported extensions are '.ts', '.tsx', '.d.ts', '.js', '.jsx'.
This can be verified by hacking into typescript.js, and manually adding the file. It's ugly, as detective work often is (starts at line 95141 in v2.6.1):
for (var _i = 0, rootFileNames_1 = rootFileNames; _i < rootFileNames_1.length; _i++) {
var fileName = rootFileNames_1[_i];
this.createEntry(fileName, ts.toPath(fileName, this.currentDirectory, getCanonicalFileName));
}
this.createEntry("c:/path/to/project/example.api", ts.toPath("c:/path/to/project/example.api", this.currentDirectory, getCanonicalFileName));
Conceptually, you're just passing a string between loaders, but it turns out the file name is important here.
A possible fix
I didn't see a way to do this with awesome-typescript-loader
, but if you're willing to use ts-loader instead, you can certainly generate TypeScript from files with arbitrary extensions, compile that TypeScript, and inject it into your output.js
.
ts-loader
has an appendTsSuffixTo option, that can be used to work around the well-known file extension pain. Your webpack config might look something like this if you went that route:
rules: [
{
test: /\.api$/,
exclude: /node_modules/,
use: [
{ loader: "ts-loader" },
{ loader: "my-own-loader" }
]
},
{
test: /\.tsx?$/,
exclude: /node_modules/,
loader: "ts-loader",
options: {
appendTsSuffixTo: [/\.api$/]
}
}
]
Note on interfaces and DX
Interfaces are erased by the compiler. This can be demonstrated by running tsc
against something like this
interface DummyContent {
name: string;
age?: number;
}
vs. this
interface DummyContent {
name: string;
age?: number;
}
class DummyClass {
printMessage = () => {
console.log("message");
}
}
var dummy = new DummyClass();
dummy.printMessage();
In order to provide a nice developer experience, you may need to write these interfaces to a file in the dev environment only. You don't need to write them out for a production build, and you don't need (or want) to check them into version control.
Developers probably need to have them written out so their IDE has something to sink its teeth into. You might add *.api.ts
to .gitignore
, and keep them out of the repository, but I suspect they'll need to exist in the developers' workspaces.
For example, in my sample repo, a new developer would have to run npm install
(as usual) and npm run build
(to generate the interfaces in their local environment) to get rid of all their red squigglies.