Skip to content

Instantly share code, notes, and snippets.

@rhalff
Created September 13, 2020 14:15
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save rhalff/0c82b3f52b641acf9d209f35fba7c2b5 to your computer and use it in GitHub Desktop.
Save rhalff/0c82b3f52b641acf9d209f35fba7c2b5 to your computer and use it in GitHub Desktop.
Generate Type Definitions for Sails Models
#!/usr/bin/env node
// Usage: node ./typeSails.js --models ./api/models -t ./typings -f
// This generates an index.d.ts in ./typings which enables Intellisense for your Sails models.
// Based on: https://github.com/jacobhenke/waterline2ts
const program = require("commander");
const chalk = require("chalk");
const includeAll = require("include-all");
const fs = require("fs");
const path = require("path");
program
.version("0.1.0")
.option(
"-m, --models <models>",
"the directory of models to convert",
process.cwd()
)
.option(
"-t, --ts-dir <tsDir>",
"The directory to output typescript interfaces",
"./ts"
)
.option("-f, --force-overwrite", "overwrite existing interfaces")
.parse(process.argv);
// don't overwrite existing files unless -f
let fileMode = "wx";
if (program.forceOverwrite) {
fileMode = "w";
}
let writePrefix = fs.realpathSync(program.tsDir);
let excludeDirs = new RegExp(
"node_modules|.git|" + writePrefix.split("/").pop()
);
writePrefix += "/";
// if the output dir doesn't exist, create it
if (!fs.existsSync(writePrefix)) {
fs.mkdirSync(writePrefix);
}
console.log(chalk.bold("modelsDir: ") + program.models + "/");
console.log(chalk.bold(" tsDir: ") + writePrefix);
const models = includeAll({
dirname: path.join(__dirname, program.models),
filter: /^([^.]+)\.(js|coffee|litcoffee)$/,
excludeDirs: excludeDirs
});
const nameMap = [];
for (let name in models) {
nameMap[name.toLowerCase()] = name;
}
let interfaceString = "declare namespace Models {";
// loop through models
for (let name in models) {
// generate interfaces
interfaceString += `\n interface ${name} extends Model {`;
// console.log(models[name]);
for (let column in models[name].attributes) {
let colOpts = models[name].attributes[column];
if (typeof colOpts == "function") {
continue; // doesn't seem like there should be functions in attributes, but idk...
}
interfaceString += `\n ${column}`;
if (!colOpts.required) {
interfaceString += "?";
}
if (colOpts.type) {
let type = translateType(colOpts.type);
interfaceString += `: ${type};`;
if (
colOpts.type === "date" ||
colOpts.type === "time" ||
colOpts.type === "datetime"
) {
interfaceString += " // " + colOpts.type;
} else if (colOpts.enum) {
interfaceString += " // enum: " + JSON.stringify(colOpts.enum);
}
} else if (colOpts.model) {
let model = colOpts.model;
if (nameMap[colOpts.model.toLowerCase()]) {
model = nameMap[colOpts.model.toLowerCase()];
} else {
console.log(
"� " +
chalk.red(
"Model " + chalk.bold(model.toLowerCase()) + " was not found"
)
);
console.dir(nameMap[colOpts.model.toLowerCase()]);
}
interfaceString += `: ${model}; // Association... See ${model}.ts`;
} else if (colOpts.collection) {
let model = colOpts.collection;
if (nameMap[model.toLowerCase()]) {
model = nameMap[model.toLowerCase()];
} else {
console.log(
"� " +
chalk.red(
"Model " + chalk.bold(model.toLowerCase()) + " was not found"
)
);
console.dir(nameMap[colOpts.model.toLowerCase()]);
}
interfaceString += `: ${model}[]; // Association via ${colOpts.via}... See ${model}.ts`;
}
}
interfaceString += "\n }\n";
}
interfaceString += "}\n\n";
let importsString = "import {Model} from 'waterline'\n\n";
let varDeclarations = "";
for (let name in models) {
varDeclarations += `declare var ${name}: Models.${name};\n`;
}
interfaceString =
"// Autogenerated via waterline2ts\n\n" +
importsString +
interfaceString +
varDeclarations;
// write to file
fs.open(writePrefix + "/index.d.ts", fileMode, (err, fd) => {
if (err) {
if (err.code === "EEXIST") {
console.log(
"❌ " +
chalk.red(
chalk.bold(writePrefix + "/index.d.ts") + " already exists..."
)
);
return;
}
return console.log(chalk.red.bold(err));
}
fs.write(fd, interfaceString, err => {
if (err) {
return console.log(chalk.red.bold(err));
}
});
});
function translateType(type) {
switch (type) {
case "integer":
case "float":
return "number";
case "date":
case "time":
case "datetime":
case "binary":
case "text":
return "string";
case "json":
case "array":
return "Array<any>";
default:
return type;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment