Blame view

node_modules/commoner/README.md 14.5 KB
ce4c83ff   wxy   初始提交
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
  Commoner [![Build Status](https://travis-ci.org/benjamn/commoner.png?branch=master)](https://travis-ci.org/benjamn/commoner)
  ---
  
  Commoner makes it easy to write scripts that flexibly and efficiently
  transpile any dialect of JavaScript into a directory structure of
  Node-compatible CommonJS module files.
  
  This task is made possible by
  
    1. a declarative syntax for defining how module source code should be
       found and processed,
    2. the use of [promises](https://github.com/kriskowal/q) to manage an
       asynchronous build pipeline, and
    3. never rebuilding modules that have already been built.
  
  The output files can be required seamlessly by Node, or served by any
  static file server, or bundled together using a tool such as
  [Browserify](https://github.com/substack/node-browserify),
  [WrapUp](https://github.com/kamicane/wrapup), or
  [Stitch](https://github.com/sstephenson/stitch) for delivery to a web
  browser.
  
  If you pass the `--relativize` option, Commoner also takes care to rewrite
  all `require` calls to use [relative module
  identifiers](http://wiki.commonjs.org/wiki/Modules/1.1#Module_Identifiers),
  so that the output files can be installed into any subdirectory of a
  larger project, and external tools do not have to give special treatment
  to top-level modules (or even know which modules are top-level and which
  are nested).
  
  Commoner was derived from an earlier, more opinionated tool called
  [Brigade](https://github.com/benjamn/brigade) that provided additional
  support for packaging modules together into multiple non-overlapping
  bundles. Commoner grew out of the realization that many tools already
  exist for bundling CommonJS modules, but that fewer tools focus on getting
  to that point.
  
  Installation
  ---
  
  From NPM:
  
      npm install commoner
  
  From GitHub:
  
      cd path/to/node_modules
      git clone git://github.com/reactjs/commoner.git
      cd commoner
      npm install .
  
  Usage
  ---
  
  Here's the output of `bin/commonize --help`:
  ```
  Usage: commonize [options] <source directory> <output directory> [<module ID> [<module ID> ...]]
  
  Options:
  
    -h, --help                               output usage information
    -V, --version                            output the version number
    -c, --config [file]                      JSON configuration file (no file means STDIN)
    -w, --watch                              Continually rebuild
    -x, --extension <js | coffee | ...>      File extension to assume when resolving module identifiers
    --relativize                             Rewrite all module identifiers to be relative
    --follow-requires                        Scan modules for required dependencies
    --cache-dir <directory>                  Alternate directory to use for disk cache
    --no-cache-dir                           Disable the disk cache
    --source-charset <utf8 | win1252 | ...>  Charset of source (default: utf8)
    --output-charset <utf8 | win1252 | ...>  Charset of output (default: utf8)
  ```
  
  In a single sentence: the `commonize` command finds modules with the given
  module identifiers in the source directory and places a processed copy of
  each module into the output directory, along with processed copies of all
  required modules.
  
  If you do not provide any module identifiers, `commonize` will process all
  files that it can find under the source directory that have the preferred
  file extension (`.js` by default). If your source files have a file
  extension other than `.js`, use the `-x` or `--extension` option to
  specify it. For example, `--extension coffee` to find `.coffee` files.
  
  Output
  ---
  
  Commoner prints various status messages to `STDERR`, so that you can see
  what it's doing, or figure out why it's not doing what you thought it
  would do.
  
  The only information it prints to `STDOUT` is a JSON array of module
  identifiers, which includes the identifiers passed on the command line and
  all their dependencies. This array contains no duplicates.
  
  Internally, each module that Commoner generates has a hash computed from
  the module's identifier, source code, and processing steps. Since this
  hash can be computed before processing takes place, Commoner is able to
  avoid processing a module if it has ever previously processed the same
  module in the same way.
  
  If you dig into [the
  code](https://github.com/reactjs/commoner/blob/5e7f65cab2/lib/context.js#L94),
  you'll find that Commoner maintains a cache directory (by default,
  `~/.commoner/module-cache/`) containing files with names like
  `9ffc5c853aac07bc106da1dc1b2486903ca688bf.js`.  When Commoner is about to
  process a module, it checks its hash against the file names in this
  directory. If no match is found, processing procedes and the resulting
  file is written to the cache directory with a new hash. If the appropriate
  hash file is already present in the cache directory, however, Commoner
  merely creates a hard link between the hash file and a file with a more
  meaningful name in the output directory.
  
  When you pass the `--watch` flag to `bin/commonize`, Commoner avoids
  exiting after the first build and instead watches for changes to
  previously read files, printing a new JSON array of module identifiers to
  `STDOUT` each time rebuilding finishes. Thanks to the caching of processed
  modules, the time taken to rebuild is roughly proportional to the number
  of modified files.
  
  Customization
  ---
  
  The `bin/commonize` script is actually quite simple, and you can write
  similar scripts yourself. Let's have a look:
  ```js
  #!/usr/bin/env node
  
  require("commoner").resolve(function(id) {
      var context = this;
  
      return context.getProvidedP().then(function(idToPath) {
          // If a module declares its own identifier using @providesModule
          // then that identifier will be a key in the idToPath object.
          if (idToPath.hasOwnProperty(id))
              return context.readFileP(idToPath[id]);
      });
  
  }, function(id) {
      // Otherwise assume the identifier maps directly to a filesystem path.
      // The readModuleP method simply appends the preferred file extension
      // (usually .js) to the given module identifier and opens that file.
      return this.readModuleP(id);
  });
  ```
  The scriptable interface of the `commoner` module abstracts away many of
  the annoyances of writing a command-line script. In particular, you don't
  have to do any parsing of command-line arguments, and you don't have to
  worry about installing any dependencies other than `commoner` in your
  `$NODE_PATH`.
  
  What you are responsible for, at a minimum, is telling Commoner how to
  find the source of a module given a module identifier, and you do this by
  passing callback functions to `require("commoner").resolve`. The script
  above uses two strategies that will be tried in sequence: first, it calls
  the helper function `this.getProvidedP` to retrieve an object mapping
  identifiers to file paths (more about this below); and, if that doesn't
  work, it falls back to interpreting the identifier as a path relative to
  the source directory.
  
  Now, you might not care about `this.getProvidedP`. It's really just a
  proof of concept that Commoner can support modules that declare their own
  identifiers using the `// @providesModule <identifier>` syntax, and I
  included it by default because it doesn't make a difference unless you
  decide to use `@providesModule`. If you don't like it, you could write an
  even simpler script:
  ```js
  #!/usr/bin/env node
  
  require("commoner").resolve(function(id) {
      return this.readModuleP(id);
  });
  ```
  The point is, it's entirely up to you to define how module identifiers are
  interpreted. In fact, the source you return doesn't even have to be valid
  JavaScript. It could be [CoffeeScript](http://coffeescript.org/), or
  [LESS](http://lesscss.org/), or whatever language you prefer to write by
  hand. Commoner doesn't care what your source code looks like, because
  Commoner allows you to define arbitrary build steps to turn that source
  code into plain old CommonJS.
  
  Let's consider the example of using LESS to write dynamic CSS
  modules. First, let's apply what we already know to give special meaning
  to `.less` files:
  ```js
  #!/usr/bin/env node
  
  require("commoner").resolve(function(id) {
      if (isLess(id))
          return this.readFileP(id);
  }, function(id) {
      return this.readModuleP(id);
  });
  
  function isLess(id) {
      return /\.less$/i.test(id);
  }
  ```
  All this really accomplishes is to avoid appending the `.js` file
  extension to identifiers that already have the `.less` extension.
  
  Now we need to make sure the contents of `.less` files somehow get
  transformed into plain old CommonJS, and for that we need
  `require("commoner").process`:
  ```js
  require("commoner").resolve(function(id) {
      if (isLess(id))
          return this.readFileP(id);
  }, function(id) {
      return this.readModuleP(id);
  }).process(function(id, source) {
      if (isLess(id))
          return compileLessToJs(source);
      return source;
  });
  ```
  How should `compileLessToJs` be implemented? At a high level, I propose
  that we generate a CommonJS module that will append a new `<style>` tag to
  the `<head>` the first time the module is required. This suggests to me
  that we need to take the CSS generated by LESS and somehow embed it as a
  string in a CommonJS module with a small amount of boilerplate JS.
  
  Here's a first attempt:
  ```js
  function compileLessToJs(less) {
      var css = require("less").render(less);
      return 'require("css").add(' + JSON.stringify(css) + ");";
  }
  ```
  Implementing a `css` module with an appropriate `add` method is an
  exercise that I will leave to the reader (hint: you may find [this
  StackOverflow answer](http://stackoverflow.com/a/524721/128454) useful).
  
  This almost works, but there's one problem: `require("less").render` does
  not actually return a string! For better or worse, it passes the compiled
  CSS to a callback function, which would make our task extremely painful
  *if Commoner were not deeply committed to supporting asynchronous
  processing*.
  
  Commoner uses promises for asynchronous control flow, so we need to return
  a promise if we can't return a string immediately. The easiest way to make
  a promise is to call `this.makePromise` in the following style:
  ```js
  #!/usr/bin/env node
  
  require("commoner").resolve(function(id) {
      if (isLess(id))
          return this.readFileP(id);
  }, function(id) {
      return this.readModuleP(id);
  }).process(function(id, source) {
      if (isLess(id)) {
          return this.makePromise(function(nodeStyleCallback) {
              compileLessToJs(source, nodeStyleCallback);
          });
      }
      return source;
  });
  
  function compileLessToJs(less, callback) {
      require("less").render(less, function(err, css) {
          callback(err, 'require("css").add(' + JSON.stringify(css) + ");")
      });
  }
  ```
  And we're done! This example was admittedly pretty involved, but if you
  followed it to the end you now have all the knowledge you need to write
  source files like `sidebar.less` and require them from other modules by
  invoking `require("sidebar.less")`. (And, by the way, embedding dynamic
  CSS modules in your JavaScript turns out to be an excellent idea.)
  
  Generating multiple files from one source module
  ---
  
  Commoner is not limited to generating just one output file from each
  source module. For example, if you want to follow best practices for
  producing source maps, you probably want to create a `.map.json` file
  corresponding to every `.js` file that you compile.
  
  Recall that normally your `.process` callback returns a string (or a
  promise for a string) whose contents will be written as a `.js` file in
  the output directory. To write more than one file, just return an object
  whose keys are the file extensions of the files you want to write, and
  whose values are either strings or promises for strings representing the
  desired contents of those files.
  
  Here's an example of generating two different files for every source
  module, one called `<id>.map.json` and the other called `<id>.js`:
  ```js
  require("commoner").resolve(function(id) {
      return this.readModuleP(id);
  }).process(function(id, source) {
      var result = compile(source);
      return {
          ".map.json": JSON.stringify(result.sourceMap),
          ".js": [
              result.code,
              "//# sourceMappingURL=" + id + ".map.json"
          ].join("\n")
      };
  });
  ```
  
  Note that
  ```js
  return {
      ".js": source
  };
  ```
  would be equivalent to
  ```js
  return source;
  ```
  so you only have to return an object when you want to generate multiple
  files. However, the `.js` key is mandatory when returning an object.
  
  For your convenience, if you have a sequence of multiple processing
  functions, the values of the object returned from each step will be
  resolved before the object is passed along to the next processing
  function, so you can be sure all the values are strings (instead of
  promises) at the beginning of the next processing function.
  
  Configuration
  ---
  
  Of course, not all customization requires modifying code. Most of the
  time, in fact, configuration has more to do with providing different
  dynamic values to the same code.
  
  For that kind of configuration, you don't need to modify your Commoner
  script at all, because Commoner scripts accept a flag called `--config`
  that can either specify a JSON file or (if `--config` is given without a
  file name) read a string of JSON from `STDIN`.
  
  Examples:
  
      bin/commonize source/ output/ main --config release.json
      bin/commonize source/ output/ main --config debug.json
      echo '{"debug":false}' | bin/commonize source/ output/ main --config
      echo '{"debug":true}' | bin/commonize source/ output/ main --config /dev/stdin
  
  This configuration object is exposed to the `.resolve` and `.process`
  callbacks as `this.config`. So, for example, if you wanted to implement
  minification as a processing step, you might do it like this:
  ```js
  require("commoner").resolve(function(id) {
      return this.readModule(id);
  }).process(function(id, source) {
      if (this.config.debug)
          return source;
      return minify(source);
  });
  ```
  Perhaps the coolest thing about the configuration object is that Commoner
  generates a recursive hash of all its properties and their values which is
  then incorporated into every module hash. This means that two modules with
  the same identifier and identical source code and processing steps will
  have distinct hashes if built using different configuration objects.
  
  Custom Options
  ---
  
  You can define custom options for your script by using the `option` function.
  
  ```js
  require("commoner").resolve(function(id) {
      return this.readModule(id);
  }).option(
      '--custom-option',
      'This is a custom option.'
  ).process(function(id, source) {
      if (this.options.customOption) {
          source = doCustomThing(source);
      }
      return source;
  });
  ```
  
  For more information of the options object available inside the `process` function see [Commander](https://github.com/visionmedia/commander.js).