enonic-wizardry
Advanced tools
Comparing version 0.0.6 to 0.0.7
"use strict"; | ||
exports.__esModule = true; | ||
var change_case_1 = require("change-case"); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var commander = require("commander"); | ||
@@ -8,41 +7,77 @@ var fs = require("fs"); | ||
var xmltools = require("./src/xmltools"); | ||
var STDOUT_FILENO = 1; // standard output file descriptor | ||
function generateInterface(filename) { | ||
var interfaceName = path.basename(change_case_1.pascalCase(filename.substring(0, filename.lastIndexOf(".")))); | ||
var xml = fs.readFileSync(filename, "utf-8"); | ||
return xmltools.createInterface(interfaceName, xml); | ||
var generator = xmltools.NewInterfaceGenerator(); | ||
function generateInterface(xmlFilename, tsFilename) { | ||
var interfaceName = xmltools.generateInterfaceName(tsFilename); | ||
var xml = fs.readFileSync(xmlFilename, "utf-8"); | ||
return generator.createInterface(interfaceName, xml); | ||
} | ||
exports.generateInterface = generateInterface; | ||
function openTsFile(filename, flags) { | ||
filename = filename.substring(0, filename.lastIndexOf(".")) + ".ts"; | ||
return fs.openSync(filename, flags); | ||
// map<directory, suffix> | ||
var directorySuffix = { | ||
parts: "-part-config", | ||
pages: "-page-config", | ||
site: "-config" | ||
}; | ||
// tries to avoid conflicting filenames | ||
function getTsFilename(filename) { | ||
var dirname = path.dirname(filename); | ||
var basename = path.basename(filename, path.extname(filename)); | ||
var closestDir = path | ||
.dirname(filename) | ||
.split(path.sep) | ||
.reverse() | ||
.slice(0, 2) | ||
.find(function (p) { return directorySuffix[p]; }); | ||
var suffix = closestDir ? directorySuffix[closestDir] : ""; | ||
return dirname + "/" + basename + suffix + ".ts"; | ||
} | ||
function replaceFileExtension(newExtension) { | ||
return function (filename) { | ||
return filename.substring(0, filename.length - path.extname(filename).length) + | ||
newExtension; | ||
}; | ||
} | ||
// XML-files in these directories will generate TypeScript interfaces when | ||
// using the --enonic-xml flag. | ||
var directories = [ | ||
"./src/main/resources/site/site.xml", | ||
"./src/main/resources/site/content-types", | ||
"./src/main/resources/site/parts", | ||
"./src/main/resources/site/pages" | ||
"src/main/resources/site/site.xml", | ||
"src/main/resources/site/content-types", | ||
"src/main/resources/site/parts", | ||
"src/main/resources/site/pages" | ||
]; | ||
function getEnonicXmlFiles() { | ||
var files = []; | ||
var _loop_1 = function (dir) { | ||
dir = path.resolve(dir); | ||
if (!fs.existsSync(dir)) { | ||
return "continue"; | ||
} | ||
var mixinDir = "src/main/resources/site/mixins"; | ||
function getEnonicXmlFiles(projectRootDir) { | ||
return directories | ||
.map(function (dir) { return path.join(projectRootDir, dir); }) | ||
.map(function (dir) { return path.resolve(dir); }) | ||
.filter(function (dir) { return fs.existsSync(dir); }) | ||
.reduce(function (result, dir) { | ||
var stat = fs.statSync(dir); | ||
if (stat.isFile()) { | ||
files.push(dir); | ||
} | ||
else if (stat.isDirectory()) { | ||
files.push.apply(files, fs.readdirSync(dir).map(function (filename) { return dir + filename; })); | ||
} | ||
return result | ||
.concat(stat.isFile() ? [dir] : []) | ||
.concat(stat.isDirectory() ? listXmlFiles(dir) : []); | ||
}, []); | ||
} | ||
function listXmlFiles(dir) { | ||
return listFiles(dir).filter(function (file) { return path.extname(file) === ".xml"; }); | ||
} | ||
function listFiles(dir) { | ||
return fs | ||
.readdirSync(dir) | ||
.map(function (file) { return path.join(dir, file); }) | ||
.reduce(function (result, dir) { | ||
var stat = fs.statSync(dir); | ||
return result | ||
.concat(stat.isFile() ? [dir] : []) | ||
.concat(stat.isDirectory() ? listFiles(dir) : []); | ||
}, []); | ||
} | ||
function consoleWriter(_) { | ||
return console.log; | ||
} | ||
function fileWriter(filename) { | ||
return function (output) { | ||
var fd = fs.openSync(filename, "w+"); | ||
fs.writeSync(fd, Buffer.from(output, "utf8")); | ||
fs.closeSync(fd); | ||
}; | ||
for (var _i = 0, directories_1 = directories; _i < directories_1.length; _i++) { | ||
var dir = directories_1[_i]; | ||
_loop_1(dir); | ||
} | ||
return files; | ||
} | ||
@@ -53,33 +88,45 @@ function exit(message) { | ||
} | ||
function collect(next, prev) { | ||
return prev.concat([next]); | ||
} | ||
function command(argv) { | ||
var cmd = new commander.Command(); | ||
cmd | ||
.option("--enonic-xml", "Use the default Enonic XML-files") | ||
.option("--write-to-file", "Write to .ts files instead of to stdout") | ||
.option("--project <dir>", "generate all xml files for the specified Enonic project") | ||
.option("--write-to-file", "write to .ts files instead of stdout") | ||
.option("-v, --verbose") | ||
.option("--mixin <file>", "add a file as a mixin", collect, []) | ||
.command("<cmd> [options] [files...]"); | ||
cmd.parse(argv); | ||
var writeToFile = cmd.writeToFile === true; | ||
var files = cmd.enonicXml ? getEnonicXmlFiles() : cmd.args; | ||
var projectMixins = cmd.project ? listXmlFiles(path.join(cmd.project, mixinDir)) : []; | ||
var mixinFiles = cmd.mixin.concat(projectMixins); | ||
for (var _i = 0, mixinFiles_1 = mixinFiles; _i < mixinFiles_1.length; _i++) { | ||
var filename = mixinFiles_1[_i]; | ||
var xml = fs.readFileSync(filename, "utf8"); | ||
generator.addMixin(filename, xml); | ||
} | ||
var rename = cmd.project ? getTsFilename : replaceFileExtension(".ts"); | ||
var write = cmd.writeToFile ? fileWriter : consoleWriter; | ||
var files = cmd.project ? getEnonicXmlFiles(cmd.project) : cmd.args; | ||
if (files.length === 0) { | ||
exit("No files"); | ||
} | ||
var notFiles = files.filter(function (f) { return !fs.existsSync(f); }); | ||
var notFiles = files.filter(function (file) { return !fs.existsSync(file); }); | ||
if (notFiles.length > 0) { | ||
var fileList = notFiles.map(function (f) { return " - " + f; }).join("\n"); | ||
var fileList = notFiles.map(function (file) { return " - " + file; }).join("\n"); | ||
exit("Files do not exist: \n" + fileList); | ||
} | ||
for (var _i = 0, files_1 = files; _i < files_1.length; _i++) { | ||
var filename = files_1[_i]; | ||
for (var _a = 0, files_1 = files; _a < files_1.length; _a++) { | ||
var xmlFilename = files_1[_a]; | ||
if (cmd.verbose) { | ||
console.error(xmlFilename); | ||
} | ||
try { | ||
var ts = generateInterface(filename); | ||
var buf = Buffer.from(ts, "utf8"); | ||
var output = !writeToFile ? STDOUT_FILENO : openTsFile(filename, "w+"); | ||
fs.writeSync(output, buf); | ||
if (writeToFile) { | ||
fs.closeSync(output); | ||
} | ||
var tsFilename = rename(xmlFilename); | ||
var tsInterface = generateInterface(xmlFilename, tsFilename); | ||
write(tsFilename)(tsInterface); | ||
} | ||
catch (err) { | ||
if (err === xmltools.MissingFieldNameError) { | ||
exit(filename + ": " + err); | ||
exit(xmlFilename + ": " + err); | ||
} | ||
@@ -86,0 +133,0 @@ throw err; |
@@ -1,2 +0,1 @@ | ||
import { pascalCase } from "change-case"; | ||
import * as commander from "commander"; | ||
@@ -7,44 +6,92 @@ import * as fs from "fs"; | ||
const STDOUT_FILENO = 1; // standard output file descriptor | ||
const generator = xmltools.NewInterfaceGenerator(); | ||
export function generateInterface(filename: string): string { | ||
const interfaceName = path.basename( | ||
pascalCase(filename.substring(0, filename.lastIndexOf("."))) | ||
); | ||
const xml = fs.readFileSync(filename, "utf-8"); | ||
return xmltools.createInterface(interfaceName, xml); | ||
function generateInterface(xmlFilename: string, tsFilename: string) { | ||
const interfaceName = xmltools.generateInterfaceName(tsFilename); | ||
const xml = fs.readFileSync(xmlFilename, "utf-8"); | ||
return generator.createInterface(interfaceName, xml); | ||
} | ||
function openTsFile(filename: string, flags: string): number { | ||
filename = filename.substring(0, filename.lastIndexOf(".")) + ".ts"; | ||
return fs.openSync(filename, flags); | ||
// map<directory, suffix> | ||
const directorySuffix: { [key: string]: string } = { | ||
parts: "-part-config", | ||
pages: "-page-config", | ||
site: "-config" | ||
}; | ||
// tries to avoid conflicting filenames | ||
function getTsFilename(filename: string): string { | ||
const dirname = path.dirname(filename); | ||
const basename = path.basename(filename, path.extname(filename)); | ||
const closestDir = path | ||
.dirname(filename) | ||
.split(path.sep) | ||
.reverse() | ||
.slice(0, 2) | ||
.find(p => directorySuffix[p]); | ||
const suffix = closestDir ? directorySuffix[closestDir] : ""; | ||
return `${dirname}/${basename}${suffix}.ts`; | ||
} | ||
function replaceFileExtension( | ||
newExtension: string | ||
): (filename: string) => string { | ||
return filename => | ||
filename.substring(0, filename.length - path.extname(filename).length) + | ||
newExtension; | ||
} | ||
// XML-files in these directories will generate TypeScript interfaces when | ||
// using the --enonic-xml flag. | ||
const directories = [ | ||
"./src/main/resources/site/site.xml", | ||
"./src/main/resources/site/content-types", | ||
"./src/main/resources/site/parts", | ||
"./src/main/resources/site/pages" | ||
"src/main/resources/site/site.xml", | ||
"src/main/resources/site/content-types", | ||
"src/main/resources/site/parts", | ||
"src/main/resources/site/pages" | ||
]; | ||
const mixinDir = "src/main/resources/site/mixins"; | ||
function getEnonicXmlFiles() { | ||
const files = []; | ||
for (let dir of directories) { | ||
dir = path.resolve(dir); | ||
if (!fs.existsSync(dir)) { | ||
continue; | ||
} | ||
function getEnonicXmlFiles(projectRootDir: string): Array<string> { | ||
return directories | ||
.map(dir => path.join(projectRootDir, dir)) | ||
.map(dir => path.resolve(dir)) | ||
.filter(dir => fs.existsSync(dir)) | ||
.reduce((result: Array<string>, dir: string) => { | ||
const stat = fs.statSync(dir); | ||
return result | ||
.concat(stat.isFile() ? [dir] : []) | ||
.concat(stat.isDirectory() ? listXmlFiles(dir) : []); | ||
}, []); | ||
} | ||
const stat = fs.statSync(dir); | ||
if (stat.isFile()) { | ||
files.push(dir); | ||
} else if (stat.isDirectory()) { | ||
files.push(...fs.readdirSync(dir).map(filename => dir + filename)); | ||
} | ||
} | ||
return files; | ||
function listXmlFiles(dir: string): Array<string> { | ||
return listFiles(dir).filter(file => path.extname(file) === ".xml"); | ||
} | ||
function listFiles(dir: string): Array<string> { | ||
return fs | ||
.readdirSync(dir) | ||
.map(file => path.join(dir, file)) | ||
.reduce((result: Array<string>, dir) => { | ||
const stat = fs.statSync(dir); | ||
return result | ||
.concat(stat.isFile() ? [dir] : []) | ||
.concat(stat.isDirectory() ? listFiles(dir) : []); | ||
}, []); | ||
} | ||
function consoleWriter(_: string): (output: string) => void { | ||
return console.log; | ||
} | ||
function fileWriter(filename: string): (output: string) => void { | ||
return (output: string) => { | ||
const fd = fs.openSync(filename, "w+"); | ||
fs.writeSync(fd, Buffer.from(output, "utf8")); | ||
fs.closeSync(fd); | ||
}; | ||
} | ||
function exit(message: string) { | ||
@@ -55,8 +102,17 @@ console.error(message); | ||
function command(argv: string[]) { | ||
function collect<T>(next: T, prev: Array<T>) { | ||
return prev.concat([next]); | ||
} | ||
function command(argv: Array<string>) { | ||
const cmd = new commander.Command(); | ||
cmd | ||
.option("--enonic-xml", "Use the default Enonic XML-files") | ||
.option("--write-to-file", "Write to .ts files instead of to stdout") | ||
.option( | ||
"--project <dir>", | ||
"generate all xml files for the specified Enonic project" | ||
) | ||
.option("--write-to-file", "write to .ts files instead of stdout") | ||
.option("-v, --verbose") | ||
.option("--mixin <file>", "add a file as a mixin", collect, []) | ||
.command("<cmd> [options] [files...]"); | ||
@@ -66,5 +122,14 @@ | ||
const writeToFile = cmd.writeToFile === true; | ||
const projectMixins = cmd.project ? listXmlFiles(path.join(cmd.project, mixinDir)) : []; | ||
const mixinFiles = cmd.mixin.concat(projectMixins); | ||
const files = cmd.enonicXml ? getEnonicXmlFiles() : cmd.args; | ||
for (const filename of mixinFiles) { | ||
const xml = fs.readFileSync(filename, "utf8"); | ||
generator.addMixin(filename, xml); | ||
} | ||
const rename = cmd.project ? getTsFilename : replaceFileExtension(".ts"); | ||
const write = cmd.writeToFile ? fileWriter : consoleWriter; | ||
const files = cmd.project ? getEnonicXmlFiles(cmd.project) : cmd.args; | ||
if (files.length === 0) { | ||
@@ -74,21 +139,19 @@ exit("No files"); | ||
const notFiles = files.filter(f => !fs.existsSync(f)); | ||
const notFiles = files.filter(file => !fs.existsSync(file)); | ||
if (notFiles.length > 0) { | ||
const fileList = notFiles.map(f => ` - ${f}`).join("\n"); | ||
const fileList = notFiles.map(file => ` - ${file}`).join("\n"); | ||
exit(`Files do not exist: \n${fileList}`); | ||
} | ||
for (const filename of files) { | ||
for (const xmlFilename of files) { | ||
if (cmd.verbose) { | ||
console.error(xmlFilename); | ||
} | ||
try { | ||
const ts = generateInterface(filename); | ||
const buf = Buffer.from(ts, "utf8"); | ||
const output = !writeToFile ? STDOUT_FILENO : openTsFile(filename, "w+"); | ||
fs.writeSync(output, buf); | ||
if (writeToFile) { | ||
fs.closeSync(output); | ||
} | ||
const tsFilename = rename(xmlFilename); | ||
const tsInterface = generateInterface(xmlFilename, tsFilename); | ||
write(tsFilename)(tsInterface); | ||
} catch (err) { | ||
if (err === xmltools.MissingFieldNameError) { | ||
exit(`${filename}: ${err}`); | ||
exit(`${xmlFilename}: ${err}`); | ||
} | ||
@@ -95,0 +158,0 @@ throw err; |
{ | ||
"name": "enonic-wizardry", | ||
"version": "0.0.6", | ||
"version": "0.0.7", | ||
"description": "Functional utility library for Enonic XP", | ||
@@ -9,5 +9,5 @@ "main": "lib/index.js", | ||
"scripts": { | ||
"clean": "rimraf lib/* es6/*", | ||
"clean": "rimraf lib/* es6/* bin/*", | ||
"build": "npm run clean && tsc && tsc -p tsconfig.es6.json && npm run build:cli", | ||
"build:cli": "tsc -outDir bin cli/xml-to-ts.ts", | ||
"build:cli": "tsc -p tsconfig.cli.json", | ||
"test": "jest", | ||
@@ -31,3 +31,2 @@ "watch:test": "jest --watch" | ||
"dependencies": { | ||
"@types/xmldom": "^0.1.29", | ||
"change-case": "^3.1.0", | ||
@@ -41,2 +40,3 @@ "commander": "^3.0.1", | ||
"devDependencies": { | ||
"@types/xmldom": "^0.1.29", | ||
"@types/jest": "^24.0.18", | ||
@@ -43,0 +43,0 @@ "@types/node": "^12.7.4", |
@@ -6,1 +6,36 @@ # Enonic Wizardry | ||
Functional utility library for Enonic XP | ||
## CLI | ||
`xml-to-ts.js` is a command line utility that can generate TypeScript interfaces | ||
for Sites, ContentTypes, Parts, and Pages from XML-files. | ||
- Build it: `npm run build` or `npm run build:cli` | ||
- Run it from the command line: `node bin/xml-to-ts.js my-xml-file.xml` | ||
### Gradle | ||
`xml-to-ts.js` can be run automatically as part of the`enonic project deploy` | ||
process. First, add the dependency with `npm install enonic-wizardry`, and then | ||
add a task to `build.gradle`, for example: | ||
```groovy | ||
task generateTypeScriptInterfaces( type: NodeTask, dependsOn: npmInstall ) { | ||
description = 'Generate TypeScript interfaces' | ||
environment = [ 'NODE_ENV': nodeEnvironment() ] | ||
args = [ '--project', '.', '--write-to-file' ] | ||
script = file( 'node_modules/enonic-wizardry/bin/xml-to-ts.js' ) | ||
} | ||
``` | ||
To run the task as part of the deploy process, add it to the `jar` block: | ||
``` | ||
jar { | ||
// ... other dependencies | ||
generateTypeScriptInterfaces | ||
// ... | ||
} | ||
``` |
30717
6
28
706
41
7
- Removed@types/xmldom@^0.1.29
- Removed@types/xmldom@0.1.34(transitive)