diff --git a/.gitignore b/.gitignore index 478ab073..06482799 100644 --- a/.gitignore +++ b/.gitignore @@ -19,3 +19,4 @@ test/elm/QuickType.elm *.log /test/elm/libsysconfcpus/ /test/runs +/dist diff --git a/.vscode/settings.json b/.vscode/settings.json index 28b4baf9..8e6b4f88 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,6 +1,7 @@ // Place your settings in this file to overwrite default and user settings. { "purescript.addNpmPath": true, + "purescript.buildCommand": "pulp build --build-path dist -- --json-errors", "editor.formatOnSave": true, "search.exclude": { "**/.git": true, @@ -17,4 +18,4 @@ "cli/dist": true }, "java.configuration.updateBuildConfiguration": "automatic" -} \ No newline at end of file +} diff --git a/@types/Config.d.ts b/@types/Config.d.ts new file mode 100644 index 00000000..b8749d3e --- /dev/null +++ b/@types/Config.d.ts @@ -0,0 +1,18 @@ +type Json = object; +type IRTypeable = Json | string; + +declare namespace Config { + export type TopLevelConfig = + | { name: string; samples: IRTypeable[] } + | { name: string; schema: Json }; + + export interface Config { + language: string; + topLevels: TopLevelConfig[]; + inferMaps?: boolean; + rendererOptions?: { [name: string]: string }; + } +} + +export = Config; +export as namespace Config; diff --git a/@types/Core.d.ts b/@types/Core.d.ts new file mode 100644 index 00000000..1f54f024 --- /dev/null +++ b/@types/Core.d.ts @@ -0,0 +1,7 @@ +declare namespace Core { + export type SourceCode = string; + export type ErrorMessage = string; +} + +export = Core; +export as namespace Core; diff --git a/@types/Data.Either.d.ts b/@types/Data.Either.d.ts new file mode 100644 index 00000000..d5891302 --- /dev/null +++ b/@types/Data.Either.d.ts @@ -0,0 +1,10 @@ +declare namespace Data_Either { + export interface Either {} + + export function isRight(either: Either): boolean; + export function isLeft(either: Either): boolean; + export function fromRight(partial: any): (either: Either) => U; +} + +export = Data_Either; +export as namespace Data_Either; diff --git a/@types/Data.Maybe.d.ts b/@types/Data.Maybe.d.ts new file mode 100644 index 00000000..c0d62383 --- /dev/null +++ b/@types/Data.Maybe.d.ts @@ -0,0 +1,9 @@ +declare namespace Data_Maybe { + export interface Maybe {} + + export function isJust(maybe: Maybe): boolean; + export function fromJust(unused: any): (maybe: Maybe) => T; + export const fromMaybe: (def: T) => (maybe: Maybe) => T; +} +export = Data_Maybe; +export as namespace Data_Maybe; diff --git a/@types/Doc.d.ts b/@types/Doc.d.ts new file mode 100644 index 00000000..ad178703 --- /dev/null +++ b/@types/Doc.d.ts @@ -0,0 +1,13 @@ +import { OptionSpecification } from "./Options"; + +declare namespace Doc { + export interface Renderer { + displayName: string; + names: [string]; + extension: string; + aceMode: string; + options: [OptionSpecification]; + } +} +export = Doc; +export as namespace Doc; diff --git a/@types/Language.Renderers.d.ts b/@types/Language.Renderers.d.ts new file mode 100644 index 00000000..a1975c26 --- /dev/null +++ b/@types/Language.Renderers.d.ts @@ -0,0 +1,10 @@ +import { Renderer } from "./Doc"; +import { Maybe } from "./Data.Maybe"; + +declare namespace Language_Renderers { + export const all: Renderer[]; + export function rendererForLanguage(language: string): Maybe; +} + +export = Language_Renderers; +export as namespace Language_Renderers; diff --git a/@types/Main.d.ts b/@types/Main.d.ts new file mode 100644 index 00000000..7668ec6a --- /dev/null +++ b/@types/Main.d.ts @@ -0,0 +1,20 @@ +import { Config } from "./Config"; +import { Either } from "./Data.Either"; +import { ErrorMessage, SourceCode } from "./Core"; + +declare namespace Main { + export function main(config: Config): Either; + + export function mainWithOptions(options: { + [name: string]: string; + }): ((config: Config) => Either); + + export function urlsFromJsonGrammar( + json: object + ): Either; + + export const intSentinel: string; +} + +export = Main; +export as namespace Main; diff --git a/@types/Options.d.ts b/@types/Options.d.ts new file mode 100644 index 00000000..78cd0224 --- /dev/null +++ b/@types/Options.d.ts @@ -0,0 +1,34 @@ +import { Maybe } from "./Data.Maybe"; + +declare namespace Options { + export interface BooleanValue { + value0: boolean; + } + + export interface StringValue { + value0: string; + } + + export interface EnumValue { + value0: number; + value1: string[]; + } + + export type OptionValue = BooleanValue | StringValue | EnumValue; + + export interface OptionSpecification { + name: string; + description: string; + typeLabel: string; + default: OptionValue; + } + + export const valueType: ( + value: OptionValue + ) => "BooleanValue" | "StringValue" | "EnumValue"; + + export const stringValue: (value: OptionValue) => Maybe; +} + +export = Options; +export as namespace Options; diff --git a/cli/Doc.d.ts b/cli/Doc.d.ts deleted file mode 100644 index b7dd99cb..00000000 --- a/cli/Doc.d.ts +++ /dev/null @@ -1,7 +0,0 @@ -interface Renderer { - displayName: string; - names: [string]; - extension: string; - aceMode: string; - options: [OptionSpecification]; -} diff --git a/cli/Main.d.ts b/cli/Main.d.ts deleted file mode 100644 index 58430ab9..00000000 --- a/cli/Main.d.ts +++ /dev/null @@ -1,27 +0,0 @@ -type SourceCode = string; -type ErrorMessage = string; - -interface Main { - main(config: Config): Either; - mainWithOptions(options: { - [name: string]: string; - }): ((config: Config) => Either); - urlsFromJsonGrammar( - json: object - ): Either; - intSentinel: string; -} - -type Json = object; -type IRTypeable = Json | string; - -type TopLevelConfig = - | { name: string; samples: IRTypeable[] } - | { name: string; schema: Json }; - -interface Config { - language: string; - topLevels: TopLevelConfig[]; - inferMaps?: boolean; - rendererOptions?: { [name: string]: string }; -} diff --git a/cli/Options.d.ts b/cli/Options.d.ts deleted file mode 100644 index 89961eb0..00000000 --- a/cli/Options.d.ts +++ /dev/null @@ -1,5 +0,0 @@ -interface OptionSpecification { - name: string; - description: string; - typeLabel: string; -} diff --git a/cli/Prelude.d.ts b/cli/Prelude.d.ts deleted file mode 100644 index 33834a5c..00000000 --- a/cli/Prelude.d.ts +++ /dev/null @@ -1,2 +0,0 @@ -type Either = { value0: L | R }; -type Maybe = { value0?: T }; diff --git a/cli/Renderers.d.ts b/cli/Renderers.d.ts deleted file mode 100644 index 988bc983..00000000 --- a/cli/Renderers.d.ts +++ /dev/null @@ -1,4 +0,0 @@ -interface Renderers { - all: Renderer[]; - rendererForLanguage(language: string): Maybe; -} diff --git a/cli/either.ts b/cli/either.ts deleted file mode 100644 index 72a7c6ce..00000000 --- a/cli/either.ts +++ /dev/null @@ -1,21 +0,0 @@ -export function isRight(either: Either): boolean { - return either.constructor.name == "Right"; -} - -export function isLeft(either: Either): boolean { - return either.constructor.name == "Left"; -} - -export function get(either: Either): T | U { - return either.value0; -} - -export function fromRight(either: Either): T | never { - let result = get(either); - if (isLeft(either)) { - console.error(result); - return process.exit(1); - } else { - return result; - } -} diff --git a/cli/maybe.ts b/cli/maybe.ts deleted file mode 100644 index 06ed6147..00000000 --- a/cli/maybe.ts +++ /dev/null @@ -1,8 +0,0 @@ -export function isJust(maybe: Maybe): boolean { - return maybe.constructor.name === "Just"; -} - -export function fromJust(maybe: Maybe): T { - if (!maybe.value0) throw "fromJust invoked on Nothing"; - return maybe.value0; -} diff --git a/cli/purescript.ts b/cli/purescript.ts new file mode 100644 index 00000000..89cb8a7e --- /dev/null +++ b/cli/purescript.ts @@ -0,0 +1,10 @@ +import * as Either from "Data.Either"; +import * as Maybe from "Data.Maybe"; + +export function fromJust(maybe: Maybe.Maybe): T { + return Maybe.fromJust(null)(maybe); +} + +export function fromRight(either: Either.Either): R { + return Either.fromRight(null)(either); +} diff --git a/cli/quicktype.ts b/cli/quicktype.ts index 181458d2..1fb97879 100755 --- a/cli/quicktype.ts +++ b/cli/quicktype.ts @@ -1,13 +1,20 @@ import * as fs from "fs"; import * as path from "path"; import * as process from "process"; -import * as Either from "./either"; -import * as Maybe from "./maybe"; -import { psRequire } from "./require"; + +import * as Either from "Data.Either"; +import * as Maybe from "Data.Maybe"; + +// These are simplified, uncurried versions of Either.fromRight, etc. +import { fromRight, fromJust } from "./purescript"; + import * as _ from "lodash"; -const Main: Main = psRequire("Main"); -const Renderers: Renderers = psRequire("Language.Renderers"); +import * as Main from "Main"; +import { Config } from "Config"; +import { Renderer } from "Doc"; +import * as Renderers from "Language.Renderers"; +import { ErrorMessage, SourceCode } from "Core"; const makeSource = require("stream-json"); const Assembler = require("stream-json/utils/Assembler"); @@ -232,7 +239,7 @@ class Run { console.error(`'${lang}' is not yet supported as an output language.`); process.exit(1); } - return Maybe.fromJust(maybe); + return fromJust(maybe); }; renderSamplesOrSchemas = ( @@ -254,7 +261,7 @@ class Run { rendererOptions: this.options.rendererOptions }; - return Either.fromRight(Main.main(config)); + return fromRight(Main.main(config)); }; splitAndWriteJava = (dir: string, str: string) => { @@ -391,7 +398,7 @@ class Run { usage(); } else if (this.options.srcUrls) { let json = JSON.parse(fs.readFileSync(this.options.srcUrls, "utf8")); - let jsonMap = Either.fromRight(Main.urlsFromJsonGrammar(json)); + let jsonMap = fromRight(Main.urlsFromJsonGrammar(json)); this.renderAndOutput( await this.mapValues(jsonMap, this.parseFileOrUrlArray) ); diff --git a/cli/require.ts b/cli/require.ts deleted file mode 100644 index c67c9a7a..00000000 --- a/cli/require.ts +++ /dev/null @@ -1,14 +0,0 @@ -export function tryRequire(...paths: string[]): any { - for (let path of paths) { - try { - return require(path); - } catch (e) { - continue; - } - } - throw "No path could be required"; -} - -export function psRequire(path: string): T { - return tryRequire(`../output/${path}`, `./${path}`); -} diff --git a/cli/tsconfig.json b/cli/tsconfig.json index 68683a3e..b4f62d06 100644 --- a/cli/tsconfig.json +++ b/cli/tsconfig.json @@ -1,10 +1,15 @@ { "compilerOptions": { "target": "es5", + "module": "commonjs", "lib": ["es2015"], - "outDir": "../output", + "baseUrl": ".", + "allowJs": false, + "declaration": true, "typeRoots": ["../node_modules/@types"], - "strictNullChecks": true - }, - "include": ["*.ts"] + "strictNullChecks": true, + "paths": { + "*": ["../dist/*"] + } + } } diff --git a/package-lock.json b/package-lock.json index 5280c445..28474041 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,6 +16,16 @@ "integrity": "sha512-wbKN0MB4XsjdnSE04HiCzLoBDirGCM6zXrqavSj44nZnPFYpnrTF64E9O6Xmf0ca/IuKK/BHUcXwMiwk92gW6Q==", "dev": true }, + "JSONStream": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", + "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", + "dev": true, + "requires": { + "jsonparse": "1.3.1", + "through": "2.3.8" + } + }, "acorn": { "version": "4.0.13", "resolved": "https://registry.npmjs.org/acorn/-/acorn-4.0.13.tgz", @@ -443,9 +453,9 @@ "integrity": "sha1-+GzWzvT1MAyOY+B6TVEvZfv/RTE=", "dev": true, "requires": { + "JSONStream": "1.3.1", "combine-source-map": "0.7.2", "defined": "1.0.0", - "JSONStream": "1.3.1", "through2": "2.0.3", "umd": "3.0.1" } @@ -473,6 +483,7 @@ "integrity": "sha1-tanJAgJD8McORnW+yCI7xifkFc4=", "dev": true, "requires": { + "JSONStream": "1.3.1", "assert": "1.4.1", "browser-pack": "6.0.2", "browser-resolve": "1.11.2", @@ -494,7 +505,6 @@ "https-browserify": "0.0.1", "inherits": "2.0.3", "insert-module-globals": "7.0.1", - "JSONStream": "1.3.1", "labeled-stream-splicer": "2.0.0", "module-deps": "4.1.1", "os-browserify": "0.1.2", @@ -595,18 +605,12 @@ "integrity": "sha1-BxPLdYckemMqnwjPG9FpuHi2Koo=", "dev": true, "requires": { - "browserify-cache-api": "3.0.1", "JSONStream": "0.10.0", + "browserify-cache-api": "3.0.1", "through2": "2.0.3", "xtend": "4.0.1" }, "dependencies": { - "jsonparse": { - "version": "0.0.5", - "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", - "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=", - "dev": true - }, "JSONStream": { "version": "0.10.0", "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-0.10.0.tgz", @@ -616,6 +620,12 @@ "jsonparse": "0.0.5", "through": "2.3.8" } + }, + "jsonparse": { + "version": "0.0.5", + "resolved": "https://registry.npmjs.org/jsonparse/-/jsonparse-0.0.5.tgz", + "integrity": "sha1-MwVCrT8KZUZlt3jz6y2an6UHrGQ=", + "dev": true } } }, @@ -2558,14 +2568,6 @@ } } }, - "string_decoder": { - "version": "1.0.1", - "bundled": true, - "dev": true, - "requires": { - "safe-buffer": "5.0.1" - } - }, "string-width": { "version": "1.0.2", "bundled": true, @@ -2576,6 +2578,14 @@ "strip-ansi": "3.0.1" } }, + "string_decoder": { + "version": "1.0.1", + "bundled": true, + "dev": true, + "requires": { + "safe-buffer": "5.0.1" + } + }, "stringstream": { "version": "0.0.5", "bundled": true, @@ -3249,10 +3259,10 @@ "integrity": "sha1-wDv04BywhtW15azorQr+eInWOMM=", "dev": true, "requires": { + "JSONStream": "1.3.1", "combine-source-map": "0.7.2", "concat-stream": "1.5.2", "is-buffer": "1.1.5", - "JSONStream": "1.3.1", "lexical-scope": "1.2.0", "process": "0.11.10", "through2": "2.0.3", @@ -3580,16 +3590,6 @@ "integrity": "sha1-T9kss04OnbPInIYi7PUfm5eMbLk=", "dev": true }, - "JSONStream": { - "version": "1.3.1", - "resolved": "https://registry.npmjs.org/JSONStream/-/JSONStream-1.3.1.tgz", - "integrity": "sha1-cH92HgHa6eFvG8+TcDt4xwlmV5o=", - "dev": true, - "requires": { - "jsonparse": "1.3.1", - "through": "2.3.8" - } - }, "jsprim": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/jsprim/-/jsprim-1.4.1.tgz", @@ -4029,6 +4029,7 @@ "integrity": "sha1-IyFYM/HaE/1gbMuAh7RIUty4If0=", "dev": true, "requires": { + "JSONStream": "1.3.1", "browser-resolve": "1.11.2", "cached-path-relative": "1.0.1", "concat-stream": "1.5.2", @@ -4036,7 +4037,6 @@ "detective": "4.5.0", "duplexer2": "0.1.4", "inherits": "2.0.3", - "JSONStream": "1.3.1", "parents": "1.0.1", "readable-stream": "2.0.6", "resolve": "1.4.0", @@ -5101,18 +5101,18 @@ "readable-stream": "2.0.6" } }, - "string_decoder": { - "version": "0.10.31", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", - "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", - "dev": true - }, "string-stream": { "version": "0.0.7", "resolved": "https://registry.npmjs.org/string-stream/-/string-stream-0.0.7.tgz", "integrity": "sha1-z83oJ5n6YvMDQpqqeTNu6INDMv4=", "dev": true }, + "string_decoder": { + "version": "0.10.31", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-0.10.31.tgz", + "integrity": "sha1-YuIDvEF2bGwoyfyEMB2rHFMQ+pQ=", + "dev": true + }, "stringstream": { "version": "0.0.5", "resolved": "https://registry.npmjs.org/stringstream/-/stringstream-0.0.5.tgz", diff --git a/package.json b/package.json index c1424812..da0f3b2a 100644 --- a/package.json +++ b/package.json @@ -2,6 +2,8 @@ "name": "quicktype", "version": "0.10.8", "license": "Apache-2.0", + "main": "quicktype.js", + "types": "quicktype.d.ts", "repository": "https://github.com/quicktype/quicktype", "scripts": { "pub": "script/publish.sh", diff --git a/script/build.sh b/script/build.sh index 38ef97a6..20ece99f 100755 --- a/script/build.sh +++ b/script/build.sh @@ -1,12 +1,46 @@ #!/bin/sh +OUTDIR=dist + npm install --ignore-scripts bower install --allow-root -pulp build -- --source-maps --stash --censor-warnings +pulp build --build-path $OUTDIR -- --source-maps --stash --censor-warnings + +# Move TypeScript typings next to PureScript JavaScript modules +for typeFile in @types/*.d.ts; do + filename=$(basename $typeFile) + module="${filename%%.d.ts}" + + cat $typeFile \ + | sed -e "s/from \"\.\/\(.*\)\"/from \"\.\.\/\/\1\"/g" \ + > $OUTDIR/$module/index.d.ts +done tsc --project cli/tsconfig.json -BIN=output/quicktype.js -echo "#!/usr/bin/env node" > $BIN.bak -cat $BIN >> $BIN.bak -mv $BIN.bak $BIN \ No newline at end of file +# Rewrite `require("Module")` as `require("./Module")` for PureScript modules +# +# TODO I hate this, but after two weeks trying to make TypeScript, PureScript, +# and JavaScript play nicely together, I'm saying "fuck it". +# +# Imports have to be absolute to compile, but relative to run. Seriously, fuck it. +for typeFile in @types/*.d.ts; do + filename=$(basename $typeFile) + module="${filename%%.d.ts}" + # TODO this currently only works for top-level files + for script in cli/*.js; do + sed -i -e "s/require(\"$module\")/require(\"\.\/$module\")/g" $script + done + for script in cli/*.d.ts; do + sed -i -e "s/from \"$module\"/from \"\.\/$module\"/g" $script + done +done + +# Distribute README, TypeScript, etc. +rm -f cli/*-e # remove artifacts from sed(?) +cp cli/*.js cli/*.d.ts $OUTDIR/ + +(echo "#!/usr/bin/env node"; cat cli/quicktype.js) > $OUTDIR/quicktype.js +chmod +x $OUTDIR/quicktype.js + +rm -f cli/*.js cli/*.d.ts \ No newline at end of file diff --git a/script/publish.sh b/script/publish.sh index 5147bc26..47611ccd 100755 --- a/script/publish.sh +++ b/script/publish.sh @@ -1,22 +1,20 @@ #!/bin/bash +OUTDIR=dist + # If not on CI, do a clean build if [ -z "$CI" ]; then - rm -rf output + rm -rf $OUTDIR npm run build fi # Copy npm package files into output/ mkdir -p output -cp LICENSE* package*.json cli/README.md output/ -cd output - -# This is pretty silly, but we do it to make Travis deploy work -mkdir script -echo "#\!/bin/bash" > script/build.sh -chmod +x script/build.sh +cp -r LICENSE* package*.json cli/README.md $OUTDIR/ +cd $OUTDIR # If not on CI, publish directly if [ -z "$CI" ]; then - npm publish + npm publish \ + --ignore-scripts # Don't rebuild fi \ No newline at end of file diff --git a/script/quicktype b/script/quicktype index 1796a074..87378612 100755 --- a/script/quicktype +++ b/script/quicktype @@ -1,3 +1,10 @@ #!/bin/bash -npx ts-node --project cli/tsconfig.json cli/quicktype.ts $@ +err() { + echo "$@" 1>&2 +} + +err "Building quicktype..." +npm run build &>/dev/null + +node dist/quicktype.js $@ diff --git a/src/Options.purs b/src/Options.purs index 950bce42..f1d7d898 100644 --- a/src/Options.purs +++ b/src/Options.purs @@ -10,10 +10,13 @@ module Options , enumOption , makeOptionValues , lookupOptionValue + , valueType + , stringValue ) where import Prelude +import Data.Array ((!!)) import Data.Array as A import Data.Foldable (elem, intercalate) import Data.Maybe (Maybe(..), fromJust) @@ -45,6 +48,18 @@ type OptionValueExtractor a = OptionValue -> a type Option a = { specification :: OptionSpecification, extractor :: OptionValueExtractor a } +valueType :: OptionValue -> String +valueType = case _ of + BooleanValue _ -> "BooleanValue" + StringValue _ -> "StringValue" + EnumValue _ _ -> "EnumValue" + +stringValue :: OptionValue -> Maybe String +stringValue = case _ of + BooleanValue b -> Just $ show b + StringValue s -> Just s + EnumValue i xs -> xs !! i + -- FIXME: error handling! makeOptionValues :: OptionSpecifications -> StrMap String -> OptionValues makeOptionValues optionSpecifications optionStrings = diff --git a/src/Samples.purs b/src/Samples.purs deleted file mode 100644 index e2e562e9..00000000 --- a/src/Samples.purs +++ /dev/null @@ -1,14 +0,0 @@ -module Samples where - -samples :: Array String -samples = [ - "getting-started.json", - "pokedex.json", - "spotify-album.json", - "kitchen-sink.json", - "bitcoin-block.json", - "reddit.json", - "github-events.json", - "us-avg-temperatures.json", - "us-senators.json" - ] diff --git a/test/test.ts b/test/test.ts index fd40401a..7f3338c6 100755 --- a/test/test.ts +++ b/test/test.ts @@ -79,7 +79,7 @@ async function main(sources: string[]) { function testCLI() { console.log(`* CLI sanity check`); - const qt = "node output/quicktype.js"; + const qt = "node dist/quicktype.js"; exec(`${qt} --help`); } diff --git a/test/tsconfig.json b/test/tsconfig.json index 7c2192d4..1bc64cf9 100644 --- a/test/tsconfig.json +++ b/test/tsconfig.json @@ -4,5 +4,5 @@ "lib": ["es2015"], "strictNullChecks": true }, - "include": ["*.ts", "../cli/*.ts"] + "include": ["*.ts", "../dist/*.js", "../dist/*.d.ts"] } diff --git a/test/utils.ts b/test/utils.ts index 9ca6d2f0..6509bd0b 100644 --- a/test/utils.ts +++ b/test/utils.ts @@ -2,7 +2,7 @@ import * as fs from "fs"; -import { main as quicktype_, Options } from "../cli/quicktype"; +import { main as quicktype_, Options } from "../dist/quicktype"; import * as languages from "./languages"; import deepEquals from "./lib/deepEquals";