Skip to content

Commit 219f4e3

Browse files
Merge pull request #121 from conveyal/dev
v3.4.0
2 parents 0d49e60 + 7a98287 commit 219f4e3

11 files changed

+445
-24
lines changed

README.md

+40
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717
* [Deploy](#deploy)
1818
* [Lint](#lint)
1919
* [Test](#test)
20+
* [Lint Messages](#lint-messages)
2021

2122
## Install
2223

@@ -71,6 +72,7 @@ $ mastarm --help
7172
deploy [entries...] [options] Bundle & Deploy JavaScript & CSS
7273
lint [paths...] Lint JavaScript [& CSS coming soon!]
7374
test [options] Run tests using Jest test runner
75+
lint-messages [paths...] Lint message strings, making sure all messages used in source files are present in messages.yml
7476

7577
Options:
7678

@@ -171,6 +173,44 @@ Options:
171173

172174
```
173175

176+
### `lint-messages`
177+
178+
```shell
179+
$ mastarm lint-messages
180+
181+
Usage: lint-messages [options] [paths...]
182+
183+
Check that all messages used in source code are present in config. Pass in path to source file(s). Set the config with --config.
184+
185+
```
186+
187+
This checks to ensure that all of the messages referenced in JS code are defined in the `messages.yml`
188+
file. It defaults to using the messages in `configurations/default/messages.yml`, however a different
189+
config can be specified with `--config`. By default it will check the JS files in `lib`, but you can
190+
also pass in an arbitrary number of paths to directories or files to lint.
191+
192+
`lint-messages` is somewhat opinionated about how messages should be used in code. They should be imported
193+
from a local module called `messages`, and referred to using dot notation. It will work regardless
194+
of whether you import the top-level messages object or named children; the following all work:
195+
196+
import messages from '../utils/messages'
197+
import msgs from './messages'
198+
import { analysis } from './messages'
199+
import msgs, { project as proj } from '../messages'
200+
import {analysis, project as proj}, msgs from '../messages'
201+
202+
and permutations thereof. Messages should be referred to directly from these top-level imports, i.e.
203+
you should not refer to messages like this:
204+
205+
import messages from './messages'
206+
const { analysis, project } = messages
207+
return analysis.newScenario
208+
209+
but the following is fine:
210+
211+
import { analysis } from './messages'
212+
return analysis.newScenario
213+
174214
[npm-image]: https://img.shields.io/npm/v/mastarm.svg?maxAge=2592000&style=flat-square
175215
[npm-url]: https://www.npmjs.com/package/mastarm
176216
[travis-image]: https://img.shields.io/travis/conveyal/mastarm.svg?style=flat-square

__tests__/bin/mastarm.js

+1-1
Original file line numberDiff line numberDiff line change
@@ -40,7 +40,7 @@ describe('mastarm cli', () => {
4040
exec(`node ${mastarm} build ${mockDir}/index.js:${buildDir}/index.js ${mockDir}/index.css:${buildDir}/index.css`,
4141
(err, stdout, stderr) => {
4242
expect(err).toBeNull()
43-
expect(stdout).toBe('')
43+
expect(stdout).toContain('updated css file')
4444
expect(stderr).toBe('')
4545
expect(fs.existsSync(`${buildDir}/index.js`)).toBeTruthy()
4646
expect(fs.existsSync(`${buildDir}/index.css`)).toBeTruthy()
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,103 @@
1+
exports[`import { analysis, project as proj } from '../utils/messages' 1`] = `
2+
Array [
3+
undefined,
4+
Array [
5+
Array [
6+
"analysis",
7+
"analysis",
8+
],
9+
Array [
10+
"project",
11+
"proj",
12+
],
13+
],
14+
]
15+
`;
16+
17+
exports[`import { analysis, project as proj }, messages from '../utils/messages' 1`] = `
18+
Array [
19+
"messages",
20+
Array [
21+
Array [
22+
"analysis",
23+
"analysis",
24+
],
25+
Array [
26+
"project",
27+
"proj",
28+
],
29+
],
30+
]
31+
`;
32+
33+
exports[`import {analysis, project as proj} from '../utils/messages' 1`] = `
34+
Array [
35+
undefined,
36+
Array [
37+
Array [
38+
"analysis",
39+
"analysis",
40+
],
41+
Array [
42+
"project",
43+
"proj",
44+
],
45+
],
46+
]
47+
`;
48+
49+
exports[`import messages, { analysis, project as proj } from '../utils/messages' 1`] = `
50+
Array [
51+
"messages",
52+
Array [
53+
Array [
54+
"analysis",
55+
"analysis",
56+
],
57+
Array [
58+
"project",
59+
"proj",
60+
],
61+
],
62+
]
63+
`;
64+
65+
exports[`import msgs from '../utils/messages' 1`] = `
66+
Array [
67+
"msgs",
68+
Array [],
69+
]
70+
`;
71+
72+
exports[`lint-messages should lint file 1`] = `
73+
Array [
74+
Array [
75+
"common.downloadRegional",
76+
6,
77+
],
78+
Array [
79+
"analysis.stop",
80+
8,
81+
],
82+
Array [
83+
"analysis.stop",
84+
9,
85+
],
86+
Array [
87+
"project.delete",
88+
12,
89+
],
90+
Array [
91+
"project.delete",
92+
13,
93+
],
94+
Array [
95+
"common.runRegional",
96+
14,
97+
],
98+
Array [
99+
"common.stopRegional",
100+
14,
101+
],
102+
]
103+
`;

__tests__/lib/lint-messages.js

+60
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
/* globals describe, expect, it */
2+
3+
const testFile = `
4+
import messages, { analysis, project as proj } from '../utils/messages'
5+
import Component, { React, PropTypes } from 'react'
6+
7+
const test = messages.common.download
8+
const test2 = messages.common.downloadRegional
9+
const test3 = <div>{analysis.run}</div>
10+
const test3b = <div>{analysis.stop}</div>
11+
const test4 = analysis.stop
12+
const test5 = proj.new
13+
// handle multiple messages in one line
14+
const test6 = test5 ? proj.delete : proj.new
15+
const test7 = test6 ? proj.new : proj.delete
16+
const test8 = test7 ? messages.common.runRegional : messages.common.stopRegional
17+
`
18+
19+
// test messages, some of the referenced messages are undefined
20+
const testMessages = {
21+
common: {
22+
download: 'Download'
23+
},
24+
analysis: {
25+
run: 'Run'
26+
},
27+
project: {
28+
new: 'New'
29+
}
30+
}
31+
32+
describe('lint-messages', () => {
33+
const { lintFileContents, parseImportLine } = require('../../lib/lint-messages')
34+
it('should parse root import correctly', () => {
35+
expect(parseImportLine("import msgs from '../utils/messages'"))
36+
.toMatchSnapshot("import msgs from '../utils/messages'")
37+
})
38+
39+
it('should parse named imports correctly', () => {
40+
expect(parseImportLine("import { analysis, project as proj } from '../utils/messages'"))
41+
.toMatchSnapshot("import { analysis, project as proj } from '../utils/messages'")
42+
43+
// make sure it works when spacing is smaller
44+
expect(parseImportLine("import {analysis, project as proj} from '../utils/messages'"))
45+
.toMatchSnapshot("import {analysis, project as proj} from '../utils/messages'")
46+
})
47+
48+
it('should parse named and default imports together correctly', () => {
49+
expect(parseImportLine("import messages, { analysis, project as proj } from '../utils/messages'"))
50+
.toMatchSnapshot("import messages, { analysis, project as proj } from '../utils/messages'")
51+
52+
// try it with the default import after the named imports
53+
expect(parseImportLine("import { analysis, project as proj }, messages from '../utils/messages'"))
54+
.toMatchSnapshot("import { analysis, project as proj }, messages from '../utils/messages'")
55+
})
56+
57+
it('should lint file', () => {
58+
expect(lintFileContents(testMessages, testFile)).toMatchSnapshot()
59+
})
60+
})

bin/mastarm

+21
Original file line numberDiff line numberDiff line change
@@ -101,6 +101,27 @@ commander
101101
engine.cli(standardOptions)
102102
})
103103

104+
commander
105+
.command('lint-messages [paths...]')
106+
.description('Check that all messages used in source code are present in config. Pass in path to source file(s). Set the config with --config.')
107+
.action(function (paths) {
108+
const lintMessages = require('../lib/lint-messages').lint
109+
const config = loadConfig(process.cwd(), commander.config, commander.env)
110+
// default to linting lib
111+
const errors = lintMessages(paths.length > 0 ? paths : ['lib'], config.messages)
112+
113+
if (errors.length > 0) {
114+
console.log(`${errors.length} missing messages`)
115+
for (const [message, file, line] of errors) {
116+
console.log(`${file} line ${line}: ${message} is not defined`)
117+
}
118+
119+
process.exit(1)
120+
} else {
121+
console.log('No missing messages found! 💃')
122+
}
123+
})
124+
104125
commander
105126
.command('prepublish [entries...]')
106127
.description('Transpile JavaScript down to ES5 with Babel')

lib/budo.js

+2-15
Original file line numberDiff line numberDiff line change
@@ -28,21 +28,8 @@ module.exports = function ({
2828
pushstate: true
2929
}
3030
if (proxy) {
31-
const httpProxy = require('http-proxy')
32-
const proxyServer = httpProxy.createProxyServer({target: proxy})
33-
proxyServer.on('error', function (err, req, res) {
34-
console.error(err.stack)
35-
res.writeHead(500, {'Content-Type': 'text/plain'})
36-
res.end(err.message)
37-
})
38-
budoOpts.middleware.push(function (req, res, next) {
39-
if (req.url.indexOf('/api') === 0) {
40-
req.url = req.url.slice(4)
41-
proxyServer.web(req, res)
42-
} else {
43-
next()
44-
}
45-
})
31+
const middlewareProxy = require('middleware-proxy')
32+
budoOpts.middleware.push(middlewareProxy('/api', proxy))
4633
}
4734
if (flyle) {
4835
const serveTiles = require('./flyle')

lib/build.js

+1
Original file line numberDiff line numberDiff line change
@@ -53,6 +53,7 @@ function buildJs ({config, entry, env, minify, outfile, watch}) {
5353
pipeline.plugin(require('watchify'), {poll: true})
5454
pipeline.plugin(require('errorify'))
5555
pipeline.on('update', bundle)
56+
pipeline.on('log', console.log)
5657
}
5758

5859
return bundle()

lib/css-transform.js

+1
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ module.exports = function ({
4242
if (results.map) {
4343
fs.writeFile(`${outfile}.map`, results.map, handleErr)
4444
}
45+
console.log(`updated css file: ${outfile}`)
4546
}
4647
return results
4748
})

0 commit comments

Comments
 (0)