Refactor scripts (#2465)

* Refactor all kinds of scripts

* Update docs to ensure linter passes
This commit is contained in:
Isiah Meadows 2019-07-27 15:12:49 -04:00 committed by GitHub
parent 62172cbe08
commit 48e7fd1711
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
23 changed files with 1695 additions and 340 deletions

View file

@ -466,7 +466,7 @@ var Modal = {
If you do it like above, you could run into issues when using it:
```js
```javascript
var MyModal = {
view: function() {
return m(Modal, {
@ -483,7 +483,7 @@ var MyModal = {
Instead, you should forward *single* attributes into vnodes:
```js
```javascript
// PREFER
var Modal = {
// ...

View file

@ -1,71 +0,0 @@
"use strict"
var fs = require("fs")
var path = require("path")
var marked = require("marked")
var layout = fs.readFileSync("./docs/layout.html", "utf-8")
var version = JSON.parse(fs.readFileSync("./package.json", "utf-8")).version
try {fs.mkdirSync("./dist")} catch (e) {/* ignore */}
try {fs.mkdirSync("./dist/archive")} catch (e) {/* ignore */}
try {fs.mkdirSync("./dist/archive/v" + version)} catch (e) {/* ignore */}
var guides = fs.readFileSync("docs/nav-guides.md", "utf-8")
var methods = fs.readFileSync("docs/nav-methods.md", "utf-8")
generate("docs")
function generate(pathname) {
if (fs.lstatSync(pathname).isDirectory()) {
fs.readdirSync(pathname).forEach(function(filename) {
generate(pathname + "/" + filename)
})
}
else if (!pathname.match(/tutorials|archive|nav-/)) {
if (pathname.match(/\.md$/)) {
var outputFilename = pathname.replace(/\.md$/, ".html")
var markdown = fs.readFileSync(pathname, "utf-8")
var anchors = {}
var fixed = markdown
.replace(/`((?:\S| -> |, )+)(\|)(\S+)`/gim, function(match, a, b, c) { // fix pipes in code tags
return "<code>" + (a + b + c).replace(/\|/g, "&#124;") + "</code>"
})
.replace(/(^# .+?(?:\r?\n){2,}?)(?:(-(?:.|\r|\n)+?)((?:\r?\n){2,})|)/m, function(match, title, nav) { // inject menu
var file = path.basename(pathname)
var link = new RegExp("([ \t]*)(- )(\\[.+?\\]\\(" + file + "\\))")
var replace = function(match, space, li, link) {
return space + li + "**" + link + "**" + (nav ? "\n" + nav.replace(/(^|\n)/g, "$1\t" + space) : "")
}
var modified = guides.match(link) ? guides.replace(link, replace) : methods.replace(link, replace)
return title + modified + "\n\n"
})
.replace(/(\]\([^\)]+)(\.md)/gim, function(match, path, extension) {
return path + (path.match(/http/) ? extension : ".html")
}) // fix links
var markedHtml = marked(fixed)
.replace(/(\W)Array<([^/<]+?)>/gim, "$1Array&lt;$2&gt;") // Fix type signatures containing Array<...>
var title = fixed.match(/^#([^\n\r]+)/i) || []
var html = layout
.replace(/<title>Mithril\.js<\/title>/, "<title>" + title[1] + " - Mithril.js</title>")
.replace(/\[version\]/g, version) // update version
.replace(/\[body\]/, markedHtml)
.replace(/<h(.) id="([^"]+?)">(.+?)<\/h.>/gim, function(match, n, id, text) { // fix anchors
var anchor = text.toLowerCase().replace(/<(\/?)code>/g, "").replace(/<a.*?>.+?<\/a>/g, "").replace(/\.|\[|\]|&quot;|\/|\(|\)/g, "").replace(/\s/g, "-");
if(anchor in anchors) {
anchor += ++anchors[anchor]
} else {
anchors[anchor] = 0;
}
return `<h${n} id="${anchor}"><a href="#${anchor}">${text}</a></h${n}>`;
})
fs.writeFileSync("./dist/archive/v" + version + "/" + outputFilename.replace(/^docs\//, ""), html, "utf-8")
fs.writeFileSync("./dist/" + outputFilename.replace(/^docs\//, ""), html, "utf-8")
}
else if (!pathname.match(/lint|generate/)) {
var encoding = (/\.(ico|png)$/i).test(path.extname(pathname)) ? "binary" : "utf-8";
fs.writeFileSync("./dist/archive/v" + version + "/" + pathname.replace(/^docs\//, ""), fs.readFileSync(pathname, encoding), encoding)
fs.writeFileSync("./dist/" + pathname.replace(/^docs\//, ""), fs.readFileSync(pathname, encoding), encoding)
}
}
}

View file

@ -215,7 +215,7 @@ m("a-scene", [
])
```
And yes, this translates to both attributes and properties, and it works just like they would in the DOM. Using [Brick's `brick-deck`](https://brick.mozilla.io/docs/brick-deck) as an example, they have a `selected-index` attribute with a corresponding `selectedIndex` getter/setter property.
And yes, this translates to both attributes and properties, and it works just like they would in the DOM. Using [Brick's `brick-deck`](http://brick.mozilla.io/docs/brick-deck) as an example, they have a `selected-index` attribute with a corresponding `selectedIndex` getter/setter property.
```javascript
m("brick-deck[selected-index=0]", [/* ... */]) // lowercase

View file

@ -51,7 +51,7 @@ $ npm install webpack webpack-cli --save-dev
```
3. Add a "start" entry to the scripts section in `package.json`.
```js
```javascript
{
// ...
"scripts": {
@ -61,7 +61,7 @@ $ npm install webpack webpack-cli --save-dev
```
4. Create `src/index.js` file.
```js
```javascript
import m from "mithril";
m.render(document.body, "hello world");
```
@ -113,7 +113,7 @@ m.render(document.body, "hello world")
Modularization is the practice of separating the code into files. Doing so makes it easier to find code, understand what code relies on what code, and test.
CommonJS is a de-facto standard for modularizing JavaScript code, and it's used by Node.js, as well as tools like [Browserify](https://browserify.org/) and [Webpack](https://webpack.js.org/). It's a robust, battle-tested precursor to ES6 modules. Although the syntax for ES6 modules is specified in Ecmascript 6, the actual module loading mechanism is not. If you wish to use ES6 modules despite the non-standardized status of module loading, you can use tools like [Rollup](https://rollupjs.org/) or [Babel](https://babeljs.io/).
CommonJS is a de-facto standard for modularizing JavaScript code, and it's used by Node.js, as well as tools like [Browserify](http://browserify.org/) and [Webpack](https://webpack.js.org/). It's a robust, battle-tested precursor to ES6 modules. Although the syntax for ES6 modules is specified in Ecmascript 6, the actual module loading mechanism is not. If you wish to use ES6 modules despite the non-standardized status of module loading, you can use tools like [Rollup](https://rollupjs.org/) or [Babel](https://babeljs.io/).
Most browser today do not natively support modularization systems (CommonJS or ES6), so modularized code must be bundled into a single JavaScript file before running in a client-side application.
@ -125,7 +125,7 @@ npm install webpack webpack-cli --save-dev
Open the `package.json` that you created earlier, and add an entry to the `scripts` section:
```
```json
{
"name": "my-project",
"scripts": {
@ -199,7 +199,7 @@ If you open bin/app.js, you'll notice that the Webpack bundle is not minified, s
You can use hooks in your production environment to run the production build script automatically. Here's an example for [Heroku](https://www.heroku.com/):
```
```json
{
"name": "my-project",
"scripts": {

View file

@ -193,7 +193,7 @@ JSX and hyperscript are two different syntaxes you can use for specifying vnodes
You can see the tradeoffs come into play in more complex trees. For instance, consider this hyperscript tree, adapted from a real-world project by [@isiahmeadows](https://github.com/isiahmeadows/) with some alterations for clarity and readability:
```js
```javascript
function SummaryView() {
let tag, posts

View file

@ -1,186 +0,0 @@
#!/usr/bin/env node
"use strict"
var fs = require("fs")
var path = require("path")
var http = require("http")
var url = require("url")
//lint rules
function lint(file, data) {
ensureCodeIsHighlightable(file, data)
ensureCodeIsSyntaticallyValid(file, data)
ensureCodeIsRunnable(file, data)
ensureCommentStyle(file, data)
ensureLinkIsValid(file, data)
}
function ensureCodeIsHighlightable(file, data) {
var codeBlocks = data.match(/```(.|\n|\r)*?```/gim) || []
codeBlocks.forEach(function(block) {
block = block.slice(3, -3)
if (block.indexOf("javascript") !== 0) {
try {if (new Function(block)) console.log(file + " - javascript block missing language tag after triple backtick\n\n" + block + "\n\n---\n\n")}
catch (e) {/*not a js block, ignore*/}
}
})
}
function ensureCodeIsSyntaticallyValid(file, data) {
var codeBlocks = data.match(/```javascript(.|\n|\r)*?```/gim) || []
codeBlocks.forEach(function(block) {
block = block.slice(13, -3)
try {new Function(block)}
catch (e) {console.log(file + " - javascript block has wrong syntax\n\n" + e.message + "\n\n" + block + "\n\n---\n\n")}
})
}
function ensureCodeIsRunnable(file, data) {
var codeBlocks = data.match(/```javascript(.|\n|\r)*?```/gim) || []
var code = codeBlocks.map(function(block) {return block.slice(13, -3)}).join(";")
//stubs
var silentConsole = {log: function() {}}
var fetch = function() {
return Promise.resolve({
json: function() {}
})
}
try {
initMocks()
var module = {exports: {}}
new Function("console,fetch,module,require", code).call(this, silentConsole, fetch, module, function(dep) {
if (dep.indexOf("./mycomponent") === 0) return {view: function() {}}
if (dep.indexOf("mithril/ospec/ospec") === 0) return global.o
if (dep.indexOf("mithril/stream") === 0) return global.stream
if (dep === "mithril") return global.m
if (dep === "../model/User") return {
list: [],
current: {},
loadList: function() {
return Promise.resolve({data: []})
},
load: function() {
return Promise.resolve({firstName: "", lastName: ""})
},
save: function() {
return Promise.resolve()
},
}
if (dep === "./view/UserList") return {view: function() {}}
if (dep === "./view/UserForm") return {view: function() {}}
if (dep === "./view/Layout") return {view: function() {}}
})
}
catch (e) {console.log(file + " - javascript code cannot run\n\n" + e.stack + "\n\n" + code + "\n\n---\n\n")}
}
function ensureCommentStyle(file, data) {
var codeBlocks = data.match(/```javascript(.|\n|\r)*?```/gim) || []
codeBlocks.forEach(function(block) {
block = block.slice(13, -3)
if (block.match(/(^|\s)\/\/[\S]/)) console.log(file + " - comment missing space\n\n" + block + "\n\n---\n\n")
})
}
function ensureLinkIsValid(file, data) {
var links = data.match(/\]\(([^\)]+?)\)/gim) || []
links.forEach(function(match) {
var link = match.slice(2, -1)
var path = (link.match(/[\w-#]+\.md/) || [])[0]
if (link.match(/http/)) {
var u = url.parse(link)
http.request({method: "HEAD", host: u.host, path: u.pathname, port: 80}).on("error", function() {
console.log(file + " - broken external link: " + link)
})
}
else if (path && !fs.existsSync("docs/" + path)) console.log(file + " - broken link: " + link)
})
}
function initMocks() {
/* eslint-disable global-require */
global.window = require("../test-utils/browserMock")()
global.document = window.document
global.m = require("../index")
global.o = require("../ospec/ospec")
global.stream = require("../stream")
global.alert = function() {}
/* eslint-enable global-require */
//routes consumed by request.md
global.window.$defineRoutes({
"GET /api/v1/users": function() {
return {status: 200, responseText: JSON.stringify([{name: ""}])}
},
"GET /api/v1/users/search": function() {
return {status: 200, responseText: JSON.stringify([{id: 1, name: ""}])}
},
"GET /api/v1/users/1/projects": function() {
return {status: 200, responseText: JSON.stringify([{id: 1, name: ""}])}
},
"GET /api/v1/todos": function() {
return {status: 200, responseText: JSON.stringify([])}
},
"PUT /api/v1/users/1": function(request) {
return {status: 200, responseText: request.query.callback ? request.query.callback + "([])" : "[]"}
},
"POST /api/v1/upload": function() {
return {status: 200, responseText: JSON.stringify([])}
},
"GET /files/icon.svg": function() {
return {status: 200, responseText: "<svg></svg>"}
},
"GET /files/data.csv": function() {
return {status: 200, responseText: "a,b,c"}
},
"GET /api/v1/users/123": function() {
return {status: 200, responseText: JSON.stringify({id: 123})}
},
"GET /api/v1/users/foo:bar": function() {
return {status: 200, responseText: JSON.stringify({id: 123})}
},
"GET /files/image.svg": function() {
return {status: 200, responseText: "<svg></svg>"}
},
})
}
//runner
function traverseDirectory(pathname, callback) {
pathname = pathname.replace(/\\/g, "/")
return new Promise(function(resolve, reject) {
fs.lstat(pathname, function(err, stat) {
if (err) reject(err)
if (stat.isDirectory()) {
fs.readdir(pathname, function(err, pathnames) {
if (err) reject(err)
var promises = []
for (var i = 0; i < pathnames.length; i++) {
pathnames[i] = path.join(pathname, pathnames[i])
promises.push(traverseDirectory(pathnames[i], callback))
}
callback(pathname, stat, pathnames)
resolve(Promise.all(promises))
})
}
else {
callback(pathname, stat)
resolve(pathname)
}
})
})
}
//run
traverseDirectory("./docs", function(pathname) {
if (pathname.indexOf(".md") > -1 && !pathname.match(/change-log|migration-|node_modules/)) {
fs.readFile(pathname, "utf8", function(err, data) {
if (err) console.log(err)
else lint(pathname, data)
})
}
})
.then(process.exit)

View file

@ -349,7 +349,7 @@ m("div", m(Component, "value", function(key) { return "child" }))
In v0.2.x, the children of DOM nodes were represented literally with no normalization aside from using the children directly if only a single array child is present. It returned a structure more like this, with the strings represented literally.
```js
```javascript
m("div", "value", ["nested"])
// Becomes:
@ -365,7 +365,7 @@ m("div", "value", ["nested"])
In v2.x, children of DOM vnodes are normalized to objects of a single consistent structure.
```js
```javascript
m("div", "value", ["nested"])
// Becomes roughly:
@ -383,7 +383,7 @@ m("div", "value", ["nested"])
If only a single text child is present on a DOM vnode, it instead sets `text` to that value.
```js
```javascript
m("div", "value")
// Becomes roughly:

View file

@ -177,7 +177,7 @@ m(m.route.Link, {
}, "link name")
```
This supports full accessibility for both `a` and `button`, via a `disabled` attribute. This ensures [no `href` attribute or `onclick` handler is set](https://css-tricks.com/how-to-disable-links/) and that an `"aria-disabled": "true"` attribute *is* set. If you are passing an `onclick` handler already, that's dropped. (You can work around this by adding it directly in a [lifecycle hook](lifecycle.md).) The `disabled` attribute is itself proxied to the element or component, so you can disable routed `<button>`s and the like.
This supports full accessibility for both `a` and `button`, via a `disabled` attribute. This ensures [no `href` attribute or `onclick` handler is set](https://css-tricks.com/how-to-disable-links/) and that an `"aria-disabled": "true"` attribute *is* set. If you are passing an `onclick` handler already, that's dropped. (You can work around this by adding it directly in a [lifecycle hook](lifecycle-methods.md).) The `disabled` attribute is itself proxied to the element or component, so you can disable routed `<button>`s and the like.
```javascript
// This does the right thing and the accessible thing for you.
@ -241,7 +241,7 @@ As a rule of thumb, RouteResolvers should be in the same file as the `m.route` c
When using components, you could think of them as special sugar for this route resolver, assuming your component is `Home`:
```js
```javascript
var routeResolver = {
onmatch: function() { return Home },
render: function(vnode) { return [vnode] },
@ -803,16 +803,13 @@ m.route(document.body, "/", {
However, realistically, in order for that to work on a production scale, it would be necessary to bundle all of the dependencies for the `Home.js` module into the file that is ultimately served by the server.
Fortunately, there are a number of tools that facilitate the task of bundling modules for lazy loading. Here's an example using [webpack's code splitting system](https://webpack.github.io/docs/code-splitting.html):
Fortunately, there are a number of tools that facilitate the task of bundling modules for lazy loading. Here's an example using [native dynamic `import(...)`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import), supported by many bundlers:
```javascript
m.route(document.body, "/", {
"/": {
onmatch: function() {
// using Webpack async code splitting
return new Promise(function(resolve) {
require(['./Home.js'], resolve)
})
return import('./Home.js')
},
},
})

View file

@ -150,7 +150,7 @@ module.exports = MyComponent
You could easily create a few unit tests for that.
```js
```javascript
var mq = require("mithril-query")
var MyComponent = require("./MyComponent")