Closed balupton closed 8 years ago
Another take. This time using Flow Type syntax and using npm scripts for the build.
{
"devDependencies": {
"assert-helpers": "^4.0.0",
"babel-cli": "^6.4.5",
"babel-plugin-syntax-flow": "^6.3.13",
"babel-plugin-transform-flow-strip-types": "^6.4.0",
"babel-preset-es2015": "^6.3.13",
"eslint": "^1.10.3",
"eslint-plugin-flowtype": "^1.0.0",
"flow-bin": "^0.20.1",
"joe": "^1.6.0",
"joe-reporter-console": "^1.2.1",
"safeps": "^6.0.0",
"yuidocjs": "^0.10.0"
},
"scripts": {
"clean": "rm -Rf es2015 esnext",
"setup": "npm install",
"setup:build": "npm install --save-dev babel-cli babel-preset-es2015 babel-plugin-syntax-flow babel-plugin-transform-flow-strip-types eslint eslint-plugin-flowtype flow-bin yuidocjs",
"compile": "npm run compile:esnext && npm run compile:es2015",
"compile:esnext": "babel source --out-dir esnext --plugins syntax-flow,transform-flow-strip-types",
"compile:es2015": "babel source --out-dir es2015 --presets es2015 --plugins syntax-flow,transform-flow-strip-types",
"verify": "npm run compile && npm run verify:eslint && npm run verify:flow",
"verify:eslint": "eslint source --plugin flowtype",
"verify:flow": "flow check",
"meta": "npm run meta:yuidoc && npm run meta:projectz",
"meta:yuidoc": "yuidoc -o yuidoc --syntaxtype js -e .js esnext",
"meta:projectz": "$npm_package_bin compile",
"prepare": "npm run clean && npm run verify && npm run meta",
"release": "npm run prepare && npm run release:tag && npm run release:push",
"release:tag": "git tag v$npm_package_version -a",
"release:push": "git push origin master && git push origin --tags",
"pretest": "npm run verify",
"test": "node --harmony $npm_package_test"
},
"editions": [
{
"description": "source ESNext code with Flow Type",
"directory": "source",
"entry": "source/lib/index.js",
"source": true
},
{
"description": "Babel compiled ESNext code",
"directory": "esnext",
"entry": "esnext/lib/index.js"
},
{
"description": "Babel compiled ES2015 code",
"directory": "es2015",
"entry": "es2015/lib/index.js",
"browser": true
}
],
"engines": {
"node": ">=0.12"
},
"preferGlobal": true,
"bin": "./bin/projectz",
"main": "./esnextguardian.js",
"test": "./es2015/test/index.js"
}
Will remove in favour of flow type comments which no longer needed a source
directory.
A cut down version of the above without flow types:
{
"scripts": {
"clean": "rimraf es2015 yuidoc",
"setup": "npm install",
"setup:build": "npm install --save-dev babel-cli babel-preset-es2015 eslint flow-bin rimraf yuidocjs && flow init",
"compile": "npm run compile:es2015",
"compile:es2015": "babel ./esnext --out-dir ./es2015 --presets es2015",
"meta": "npm run meta:yuidoc && npm run meta:projectz",
"meta:yuidoc": "yuidoc ./esnext -o yuidoc --syntaxtype js -e .js",
"meta:projectz": "./bin/projectz compile",
"prepare": "npm run clean && npm run compile && npm run test && npm run meta",
"release": "npm run prepare && npm run release:tag && npm run release:push",
"release:tag": "git tag v$npm_package_version -a",
"release:push": "git push origin master && git push origin --tags",
"pretest": "npm run test:eslint && npm run test:flow",
"test:eslint": "eslint ./esnext",
"test:flow": "flow check",
"test": "node --harmony ./es2015/test/index.js"
},
"editions": [
{
"description": "source ESNext code",
"directory": "esnext",
"entry": "esnext/lib/index.js",
"source": true,
"prefer": true
},
{
"description": "Babel compiled ES2015 code",
"directory": "es2015",
"entry": "es2015/lib/index.js",
"browser": true,
"fallback": true
}
]
}
Turns out node actually supports creating a file at project/esnext/index.js
then including it via require('project/esnext')
. Which can simplify a lot of this.
The directory
property here can probably be optional, determined either from the edition name (if using an object hash, e.g. "editions": { "esnext": { /* ... */ } }
), or as the first directory of the entry
property. However, if optional, will require parsers to do more.
Here is another take. Uses a syntax
array for parsers to decide if they can support that edition (useful for bundlers, and parsers). Uses source
to indicate which one is the original source (useful for bundlers). Uses the order in which they appear as the require preference (first is most preferred require).
Expanded:
{
"editions": {
"source": {
"source": true, // meta information only, not used by parsers
"syntax": ["ESNext", "Flow Type", "JSX"],
"directory": "source", // meta information only, not used by parsers
"entry": "source/index.js"
},
"esnext": {
"compiled": true, // meta information only, not used by parsers
"syntax": ["ESNext"],
"directory": "esnext", // meta information only, not used by parsers
"entry": "esnext/index.js"
},
"es2015": {
"compiled": true, // meta information only, not used by parsers
"syntax": ["ES2015"],
"directory": "es2015", // meta information only, not used by parsers
"entry": "es2015/index.js"
},
"es5": {
"compiled": true, // meta information only, not used by parsers
"syntax": ["ES5"],
"directory": "es5", // meta information only, not used by parsers
"entry": "es5/index.js"
}
}
}
Shortended:
{
"editions": {
"source": {
"syntax": ["ESNext", "Flow Type", "JSX"]
},
"esnext": {
"syntax": ["ESNext"]
},
"es2015": {
"syntax": ["ES2015"]
},
"es5": {
"syntax": ["ES5"],
}
}
}
Using hashes for the editions could provide the false assumption that the edition hashes actually mean something, which isn't true, would just be for the convenience for the developer. So using an array like so would be better.
Expanded:
{
"editions": [
{
"source": true,
"syntax": ["ESNext", "Flow Type", "JSX"],
"directory": "source",
"entry": "source/index.js"
}, {
"compiled": true, // meta information only, not used by parsers
"syntax": ["ESNext"],
"directory": "esnext", // meta information only, not used by parsers
"entry": "esnext/index.js"
}, {
"compiled": true, // meta information only, not used by parsers
"syntax": ["ES2015"],
"directory": "es2015", // meta information only, not used by parsers
"entry": "es2015/index.js"
}, {
"compiled": true, // meta information only, not used by parsers
"syntax": ["ES5"],
"directory": "es5", // meta information only, not used by parsers
"entry": "es5/index.js"
}
]
}
Shortened:
{
"editions": [
{
"syntax": ["ESNext", "Flow Type", "JSX"],
"directory": "source"
}, {
"syntax": ["ESNext"],
"directory": "esnext"
}, {
"syntax": ["ES2015"],
"directory": "es2015"
}, {
"syntax": ["ES5"],
"directory": "es5"
}
]
}
There should probably also be an optional default
property that could be auto-detected, but when specified will say this edition is the one main
points to. If main
points to something else, like esnextguardian or whatever, it is suitable for no edition to have a default
property. The README generator can detect this appropriately when outputting human readable usage instructions.
There could also be the syntax features ES Modules
and CJS Modules
. However, that will require different builds for each. Maybe the proposed jsm
file extension here could work (the proposal being where each compiled file comes in two files, a .js
file for CJS Modules and a .jsm
file for ES Modules) however syntax
may need to support something like ["ESNext", ["CJS Modules", "ES Modules"]]
to say it supports either, however that is really messy and complex, and could mean multiple things - are they both supported in the same file? does it use jsm files (which the parser may or may not actually support)? Maybe using jsm files will need a new optionalSyntax
so something like "syntax": ["ESNext", "CJS Modules"], "optionalSyntax": ["JSM Modules"]
Another point, is the directory
actually useful here, or could we just have the entry
. And could the entry
just be say dir
when dir/index.js
is used, considering one can just do require('dir')
rather than require('dir/index.js')
- or should it always point to the full file path, rather than the possible shorter require path.
Output into READMEs for this proposal would be something like the following when using esnextguardian:
Editions
require('project')
is an alias forrequire('project/esnextguardian.js')
which uses esnextguardian to load the correct edition for your environmentrequire('project/source')
is the source files with ESNext, Flow Type, and JSX syntaxrequire('project/esnext')
is compiled files with ESNext syntaxrequire('project/es2015')
is compiled files with ES2015 syntaxrequire('project/es5')
is compiled files with ES5 syntax
And if not using esnextguardian and main
just goes to the ES2015 edition:
Editions
require('project')
is an alias forrequire('project/es2015')
require('project/source')
is the source files with ESNext, Flow Type, and JSX syntaxrequire('project/esnext')
is compiled files with ESNext syntaxrequire('project/es2015')
is compiled files with ES2015 syntaxrequire('project/es5')
is compiled files with ES5 syntax
Using the syntax
feature, tools like projectz could automatically add the necessary browser properties:
{
"browser": "./es5/index.js", // browserify, and perhaps webpack
"jsnext:main": "./esnext/index.js", // rollup
"jspm": { // jspm
"main": "./es5/index.js" // when jspm 0.17 comes out, projectz can automatically update this to point to esnext
}
}
The es5
edition here is babel compiled with 2015-loose preset, which is needed for things like node 0.10 and IE8 support
Other syntaxes instead of ESNext
could also be things like TypeScript
or CoffeeScript
or whatever.
Enacted at https://github.com/bevry/editions
Base files updated.
It would be nice to introduce a concept of editions for packages that include multiple targets. Something like:
Which using something like the following inside projectz:
Will generate a README entry like: