Skip to content
This repository has been archived by the owner on Aug 15, 2018. It is now read-only.

spm@1.x 旧版文档备份 #808

Closed
afc163 opened this issue Jun 4, 2014 · 29 comments
Closed

spm@1.x 旧版文档备份 #808

afc163 opened this issue Jun 4, 2014 · 29 comments
Milestone

Comments

@afc163
Copy link
Member

afc163 commented Jun 4, 2014

原来的 wiki 迁移到这里后全部删除,避免误导

注意:以下是 spm@1.x 版本的文档,已不再维护更新。

@afc163 afc163 added this to the 3.0 milestone Jun 4, 2014
@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

Hello spm:使用 spm 和 SeaJS 开发一个中型项目

现在我们要正儿八经开始开发一个叫 hello-spm 的项目了,别笑,虽然只是个为了让你更好的了解 SPM 而杜撰出来的项目,但也五脏俱全。

我们提供了在线演示:http://fool2fish.github.com/hello-spm
毫不犹豫的猛击空格键,就会有有趣的事情发生了。

你可以检出源码:https://github.com/spmjs/hello-spm

在正式开始之前,我还是要啰嗦一句,所有的范例代码都是基于 SeaJS 的, 请确保你已经知道该怎么使用 SeaJS 了。

创建项目目录

我们先来看看项目的整体结构

hello-spm/
    assets/
        hello/
        util/
        sea-modules/
        package.json
    index.html          
  • 考虑到真实的项目通常包含前后端的代码,所有前端代码放在 assets/ 。
  • hello 和 util 是为本项目编写的模块。
  • sea-modules/ 用于存放安装和部署的模块。如果你的项目基本都是基于 SeaJS 和 SPM 的,你可以把这个目录放置在和项目目录平级的路径下,以便各项目公用。
  • package.json 为模块打包部署的总体配置。
  • index.html 为项目页面。

安装需要的模块

显然,我们需要用到 seajs 和 jquery,所以要使用 spm install 来安装他们。

打开命令行工具,将路径切换至 sea-modules/ ,运行:

spm install seajs
spm install gallery.jquery@1.8.3

这时你会发现 sea-module/ 中多了 seajs 和 jquery 两个模块。其中 seajs 为最新版本, 而 jquery 则是我们安装时指定的 1.8.3 版本。其中 jquery 目前的 root 是 gallery,所以会增加一个 gallery 目录。

创建模块

回想一下 index.html 运行的效果,多处用到了随机数,例如 "hello spm !" 的单个字符大小,字符串出现在页面中的位置以及字符串在页面上停留的时间。

我们把这样一个可产生指定范围的随机整数的工具方法放到 util 模块中。

下面我们来 初始化 这个模块。

命令行工具路径切换至 util/ ,运行:

spm init

spm 会在 util/ 中创建以下文件:

util/
    examples/
    src/
    tests/
    package.json
    README.md

其中:

  • examples/ 用于存放演示文件。
  • src/ 为模块源码,打包后的文件会存放到和 src/ 平级的 dist/ 目录下。
  • test/ 用于存放测试用例,你可以使用 jasmine 等工具来保障代码的质量。
  • package.json 为本模块打包部署的配置。

我们的 hello-spm 实在有点简单,所以演示代码仅保留了 src/ 和 package.json 这两个必备部分。

src/ 中只有 util.js 一个文件, 源码非常简单,如下:

define(function(require, exports) {
    exports.random = function(min, max){
        return min + Math.round(Math.random() * (max - min))
    }
})

拆分子模块

接下来我们处理相对复杂一些的 hello 模块,他要做这么几件事情:

  • 创建字符大小随机的 "Hello SPM !" 字符串。
  • 随机显示到页面上。
  • 一段时间后自动消失。

这个模块复杂到需要拆分成多个子模块来进行开发 (好吧,我承认这纯粹是教程需要)。

命令行工具路径切换至 hello/ ,运行:

spm init

使用 spm init 初始化后,在 src/ 中除了默认创建的 hello.js,还需要手工创建一个 handle-text.js 文件。

hello.js 完成大部分的主体功能,而 handle-text.js 专门负责处理传入字符串的随机字符大小。

hello.js 的源码如下:

define(function(require, exports, module) {
    var $ = require('$')
    var random = require('util').random
    var handleText= require('./handle-text')

    function Hello(){
        this.render()
        this.bindAction()
        seajs.log('new Hello() called.')
    }

    Hello.prototype.render = function(){
        this.el = $('<div style="position:fixed;' 
            + 'left:' + random(0,70) + '%;'
            + 'top:' + random(10,80)+ '%;">'
            + handleText('Hello SPM !')
            + '</div>').appendTo('body')
    }

    Hello.prototype.bindAction = function(){
        var el = this.el
        setTimeout(function(){ el.fadeOut() }, random(500,5000))
    }

    module.exports = Hello
})

注意

  • var random = require('util').random 是模块方法的引用。
  • var handleText= require('./handle-text') 是子模块的引用。
  • 在 Hello 类的构造器中,我们还使用 seajs.log 打了一句日志。

更多 require 的说明可查看 SeaJS 的 模块标识

handle-text.js 的源码如下:

define(function(require, exports, module) {
    var $ = require('$')
    var random = require('util').random

    function handleText(text){
        var min = random(30,70)
        var max = random(50,120)
        var rt = ''
        for(var i = 0, len = text.length; i < len; i++){
            rt += '<span style="font-size:' + random(min, max) + 'px;">' + text[i] + '</span>'
        }
        return rt
    }

    module.exports = handleText
})

编写开发时页面

开发过程中,我们就常常需要编写一些测试用例,或者演示页面。

这种情况下我们希望模块是不需要打包的,并且可以查看日志,以便调试。

本例中,我们并没有为单个模块创建单元测试或者演示页面。简单起见,我们在 index.html 页面中编写了一些开发时的代码,代码如下:

<div style="text-align:center;font-size:48px;color:#999;"> Press the space key !</div>
<script src="assets/sea-modules/seajs/1.3.0/sea-debug.js"></script>
<script>
    seajs.config({
        alias:{
            '$':'gallery/jquery/1.8.3/jquery.js',
            'util':'../../util/src/util.js'
        }
    })
    seajs.use(['$','./assets/hello/src/hello.js'],function($, Hello){
         $(document).keypress(function(ev){
            if(ev.which == 32){
                new Hello()
            }
        })
    })
</script>

注意

  • 引用 sea-debug.js,可以开启 debug 功能。
  • jquery(即 $ )作为 DOM 操作最常使用的模块,总是需要通过 seajs.config 手工配置别名,确保项目内的统一。比如 1.8.3 版本。
  • seajs.use 中的 '$' 为顶级 模块标识,相对于 seajs 的 base 路径, './assets/hello/src/hello.js' 为相对标识,相对于当前页面 url。

现在运行 index.html 就能看到效果了,源码上做任何的改动也能立马体现,打开调试工具,你还能看到打出的 log。

打包部署

打包配置

结束编码测试工作后,我们就要准备将模块打包部署,以供正式环境使用了。打包相关的配置都写在每个模块的 package.json 中了。

先来看看 util 模块的 package.json

{
    "name":"util",
    "parent":"../package.json",
    "output":{"util.js":"."}
}
  • name 为模块名
  • parent 指定用于继承的父级配置,路径相对于该配置文件。当你项目中的模块有大量公共配置的时候推荐使用此项.
  • output 指定了将 src/util.js 及其模块内的依赖打包为 dist/util.js 。当然,这里 src/util.js 没有模块内依赖。

再接着看 hello 模块的 package.json

{
    "name":"hello",
    "parent":"../package.json",
    "output":{"hello.js":"."},
    "dependencies": {
        "$": "$",
        "util":"hellospm/util/0.0.1/util"
    }
}

然后我们还有一个 parent package.json

{
    "root": "hellospm",
    "version": "0.0.1"
}

其中由于我们 hello 和 util 模块的 root 和 version 是一样的,我们就可以把他放到 parent 的配置中。

绝大部分和 util 模块的配置一样,只是多了一项 dependencies,需要注意的是:

jquery 作为一个特殊的模块,打包的时候并不指定具体的依赖,仅写上 "$": "$" 即可。

回顾一下,就是因为这个原因,我们在刚刚创建的 index-debug.html 中,还需要为 jquery 配置别名: '$':'gallery/jquery/1.8.3/jquery.js' 。

接下来我们将以 util 模块为例讲解模块的打包部署(hello 模块的打包部署方式完全一样)。

打包

命令行进入到 util/ ,运行:

spm build

SPM 会在 util/dist/ 目录创建 util.js 和 util-debug.js 两个文件。有兴趣的读者可以打开 util-debug.js 看看打包后的文件和源码有何不同。

部署

为了方便演示,我们准备把打包好的模块部署到本地。

进入 util/ ,运行命令:

spm deploy --to=../sea-modules

这时 sea-modules/ 会新增 util 模块:

sea-modules/
    hellospm/
        util/
        0.0.1/
            util.js
            util-debug.js
    ...

编写正式页面

胜利就在眼前,我们终于要完成这个项目了。现在我们要把测试用的 index.html 转换成线上正式运行的 index.html,代码如下:

<div style="text-align:center;font-size:48px;color:#999;"> Press the space key !</div>
<script src="assets/sea-modules/seajs/1.3.0/sea.js"></script>
<script>
    seajs.config({
        alias:{
            '$':'gallery/jquery/1.8.3/jquery.js'
        }
    })
    seajs.use(['$','./assets/sea-modules/hellospm/hello/0.0.1/hello.js'],function($, Hello){
         $(document).keypress(function(ev){
            if(ev.which == 32){
                new Hello()
            }
        })
    })
</script>

注意 index.html 前后的一些区别:

  • index 引用的是 sea.js。
  • SPM 在对模块进行打包部署后,依赖关系已经处理好了,所以这里我们只用在 seajs.config 中配置 jquery($) 。

结束语

如果你耐心的看到这,那么恭喜你!现在你已经很了解 SeaJS 和 SPM 最常用的功能了。接下来,赶紧去自己的工作中去实践一把吧,有任何建议和反馈欢迎反馈给我们:)

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

package.json

package.json 位于模块的根目录下,提供模块的信息以及 spm 的打包配置。一个较完整的 package 文件看起来如下:

{
    "name": "ninja",
    "version": "1.0.0",
    "license": "MIT", 
    "author": "name <name@email.com>",
    "homepage": "http://example.com/project/ninja",
    "description": "description of ninja",
    "bugs": {
        "email": "your@email.com",
        "url": "https://github.com/name/ninja/issues"
    },
    "root": "test",
    "repository": {
        "type": "git",
        "url": "https://github.com/name/ninja"
    },
    "tests": ["ninja", "ninja-model"],

    "sources": ["http://modules.spmjs.org"],
    "devDependencies": {
        "base": "arale/base/1.1.8/base",
        "overlay": "arale/overlay/develop/overlay",
        "jquery": "gallery/jquery/1.7.2/jquery",
        "templatable": "arale/widget/1.7.3/templatable"
    },

    "parent": "../package.json",
    "dependencies": {
        "overlay": "arale/overlay/1.2.1/overlay",
        "jquery": "gallery/jquery/1.7.2/jquery",
        "templatable": "arale/widget/1.7.2/templatable" 
    },
    "combine": {
        "all.js": ["a.js", "b.js", "http://***/c.js"]
    },
    "output": {
        "a.js": ".",
        "a-aio.js": "*", 
        "d.js": ["d.js"]
    },
    "plugins": {
        "deploy": "spm/alipay-deploy/1.0.0/deploy",
        "test": {
            "main": "spm/lifecycle/1.0.0/p1",
            "lifecycle": true
        }
    }
}

通用配置

  • name: 模块名,只能包含小写字母数字和中划线。
  • version: 版本号,由 3 组数字组成 major.minor.bugfix 。尚在开发阶段时版本号应该加上后缀 -dev,例如 1.1.3-dev。需要手工修改,不支持根据规则自动变化。
  • license: 模块使用的授权许可协议版本或者内容。
  • author: 模块的作者。
  • homepage: 模块的项目主页。
  • description: 模块的描述。
  • bugs: bug 问题 的反馈地址信息。javascript对象,可在对象中自定义除email、url等其他地址类型信息,比如电话、QQ等。
  • root: 模块的根目录名。 设置了 root 为 handy 时,那么打包后的模块 id 是: handy/detect/1.0.0/detect.js 。
  • repository: 源码仓库。
  • tests: 测试用例。

开发时配置

  • sources: 配置 ,可以指定多个。依赖的模块将从这些指定的源中进行查找。默认为 http://modules.seajs.org。
  • devDependencies: 开发时的模块依赖,仅需配置和 dependencies 不一样的选项。页面当中引用的 seajs 开启 debug 功能时,加载模块前会先读这个模块的 package.json,分析和获取依赖。这样每个模块可以管理自己开发时的依赖,不需要使用 seajs.config() 来统一配置。

打包发布时配置

  • plugins: 用户自定义生命周期,加载插件 查看详情
  • parent: 父级配置文件,对于多个模块公共部分我们可以抽象一个公用的配置。
  • dependencies: 模块依赖, 打包过程中据此进行依赖替换。可参考 seajs.config 中的 alias 配置。
  • output: 模块输出规则。如不指定,则自动打包和模块同名的 js 及其模块内依赖。点此 查看详情
  • combine: 模块预合并配置。查看详情.
  • spmConfig: 命令行参数配置。点此 查看详情

注意 :如果你的 repository 里有多个模块你应该指定一个 path:

"repository": {
    "type": "git",
    "url": "http:github.com/name.repo.git",
    "path": "path/to/your/module"
}

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

package.json : output

目前整个配置文件中最复杂的一个部分就是 output 配置,目前支持很多输出规则,而且也比较灵活,后续还会支持用户进行扩展。下面的内容会详细的说明具体的输出规则。

假定一个模块叫 outputTest,文件结构如下:

outputTest/
    dist/
    src/
        a.js
        b.js
        c.js
    package.json
    ...

其中 package.json 内容如下:

{
    "name": "outputTest",
    "root": "test",
    "version": "0.0.1",
    "dependencies": {
      "widget": "arale/widget1.0.0/widget",
      "jquery": "gallery/jquery/1.7.2/jquery",
      "$": "gallery/jquery/1.7.2/jquery"
    },
    "output": {
      ...
    }
}

依赖关系:a 依赖 b 和 jQuery,b 依赖 c

默认合并

"output":{}

build 后输出:

dist/
    a.js
    a-debug.js
    b.js
    b-debug.js
    c.js
    c-debug.js

模块内合并依赖

"output":{"a.js": "."}

build 后输出:

dist/
    a.js
    a-debug.js

注意
配置中 "output":{"a.js": "."} 的 a.js 必须是在 src 中存在的文件名。 SPM 会打包 a.js 和其所有的包内依赖。打包后的 a.js 包含 src 目录中 a.js, b.js 和 c.js 的内容。

全依赖合并

"output":{"a.js": "*"}

模块内合并依赖类 类似,不同的是输出的 a.js 除了包含 b.js 和 c.js 外,还会包含 jQuery 的内容。

注意
这个和上面的内部合并依赖类似,配置中的 a.js 必须是在 src 中存在的文件名

数组依赖合并

 "array-merge.js": ["./a.js", "./b.js"]

将 a.js 和 b.js 打包成 array-merge.js。

复合规则的合并

上面我们看到的都是基本类型的合并方式,为了支持更加复杂的合并规则,我们也支持复合对象的合并规则。

指定排除规则的合并

通过这种方式我们可以排除某些模块

"a.js": {
    "includes": ".",
    "excludes": ["./b.js"]
}
"a.js": {
    "includes": "*",
    "excludes": ["#widget/1.0.0/widget"]
}
  1. 和模块内合并规则基本一样,不过在合并的内容中排除了 a.js 这个内部模块。
  2. includes 的规则和上面的基本规则一样。
"*": {
    "excludes": ['#jquery/1.7.2/jquery']
},
"a.js": {
    "includes": "*",
},
"b.js": "*"

我们也支持全局形式的模块排除,这样对于a.js, 还是 b.js 都会被排除 jquery 这个模块。

自定义模块文件的依赖合并

有些时候,我们输出的模块文件并不存在与我们的源文件中,这个时候就需要用到下面这种方式了。

"new_module.js": {
    "main": "./a.js",
    "includes": "*",
    "excludes": ["#widget/1.0.0/widget"]
}

build 后输出:

dist/
    new_module.js
    new_module-debug.js

通过配置 main 这种方式,我们可以输出一个并不在源文件中存在的模块。

注意
main 这个配置是我们进行依赖分析的基础模块。

更加负责的 includes 规则

有些时候,我们不光想合并某个模块的依赖,还想合并单独的几个文件,这个也是支持的.

"new_module2.js": {
    "main": "./allMerge.js",
    "includes": ["js/*js", "*"]
}
  1. 通过 main 指定主模块
  2. 首先我们会根据 js/*.js 可以统配多个文件。并合并起来
  3. 然后在合并我们的 allMerge.js 这个模块的所有依赖的模块。

资源文件输出

我们也支持资源文件的输出

"sites": "sites/**/*.js"

css文件输出

"a.css": ["./b.css", "./c.css"],
"b.css": "default"

目前css文件功能比较简单,仅支持简单的合并。

批量文件的模块输出 (新加功能,~1.0.0)

有些情况下我们要输出的模块比较多,可以通过在key上面使用通配符处理。
比如我们的源文件目录src 下面有4个模块,a.js, b.js, c.js, d.js. 原来需要这样配置:

"output": {
  "a.js": "default", // ==> ["./a.js"]
  "b.js": "default", // ==> ["./b.js"]
  "c.js": "default", // ==> ["./c.js"]
  "d.js": "default"  // ==> ["./d.js"]
}

这样的话比较繁琐,所以我们可以通过下面一种简化的方式:

"output": {
  "*.js": "default"
}
对于输出模块的匹配,我们支持glob的语法,具体的语法详情可以看下面的提示. 不过对于匹配模块的合并规则,目前主要就是这种默认的方式,使用`.,*`等合并模块规则的时候需要注意他们之间的依赖关系,如果输出的模块存在相互依赖的话,可能合并的时候会有问题.

这样的配置则会自动转换成上面的形式的.

关于资源文件输出,和模块排除,批量模块输出,我们支持glob的语法形式,所以相关语法可以参看.

https://github.com/isaacs/node-glob


下面是上面一个合并规则的汇总。需要注意的地方就是 **对于 includes, excludes 这些规则中的内部模块一定要以 ./ 开头。来表明他的位置。

{
    "name": "outputTest",
    "root": "test",
    "version": "0.0.1",
    "dependencies": {
      "widget": "arale/widget/1.0.2/widget",
      "jquery": "gallery/jquery/1.7.2/jquery",
      "$": "gallery/jquery/1.7.2/jquery"
    },
    "output": {
      "new_module.js": {
        "main": "./allMerge.js",
        "includes": ["./a.js", "./b.js", ".", "./c.js"]
      },
      "new_module2.js": {
        "main": "./allMerge.js",
        "includes": ["js/*js", "*"]
      },
      "localMerge.js": ".",
      "allMerge.js": "*",
      "arrayMerge.js": ["./arrayMerge.js", "./a.js", "./b.js", "./c.js"],
      "excludeMergeA.js": {
        "includes": ".",
        "excludes": ["./a.js"]
      },
      "excludeMergeB.js": {
        "includes": "*",
        "excludes": ["arale/widget/1.0.2/widget"]
      },
      "*": {
        "excludes": ["gallery/jquery/1.7.2/jquery", "./plugins/p2.js"]
      },
      "sites1": "sites/**/*.js",
      "plugins/*.js": "default"
    }
}

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

package.json : spmConfig

我们在打包的时候可以通过命令行参数的形式对 spm 的操作进行控制,也可以通过配置的方式进行。

"name": "module",
"version": "1.0.0",
"spmConfig": {
    "*": {
        "idRule": "{{moduleName}}",
        "with-debug": "",
        "throwErrorOnDepNotFound": true
    },
    "build": {
        "flag": "123",
        "src": "app",
        "to": "dist",
        "extra-resources":  ["src", "examples", "test"],
        "skip": "dependencies,min"
    },
    "deploy": {
        "to": "public/{{version}}/{{spmConfig.build.flag}}"
    }
}

选项含义

  • extra-resources 可以打包额外的资源文件 @1.2.0
  • skip build 的时候,可以跳过某些插件的执行 @1.4.0
  • idRule 默认规则为 {{root}}/{{name}}/{{version}}/{{moduleName}}

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

spm js 模块生命周期解析

js 模块的解析流程

目前解析是通过定义模块的生命周期来完成的,下面主要分析 js 模块默认处理流程。

资源输出(resources)

  • 资源输出(resources).我们在处理源码前,我们会把文件输出到 _build 一个临时目录中,然后后续对文件的操作都会在这个目录进行。我们在输出中也可以进行一些简单的变量替换.
  • 预合并(combine). 我们允许对一个复杂的模块,进行物理拆分,也就是说拆分后的代码只有合并到一起了,才是一个完整的模块。

编译(compile)

  • 我们目前支持 coffee, less 等文件格式的处理
  • 我们还允许对第三模块的 transport
  • json 检查(json). 对 js 模块的处理。

模块分析(analyse)

在这个阶段我们主要进行代码的语法检查(lint),依赖分析(dependencies),依赖冲突检查(depCheck)等操作.

依赖分析(dependencies)

  • spm 会分析 _build 目录中的 js, 然后会计算出来这个模块的依赖模块列表,包括全局的和相对模块
  • 对与外部模块,还回去分析模块的依赖
  • 有些模块是合并模块,spm 也会对依赖重复进行排除.
  • 对于外部模块的依赖列表进行全局化处理,也就是把原来的相对关系,也改成绝对关系.

依赖冲突检测(depCheck)

主要检查统一模块中间不同版本的冲突检查.

预打包(preBuild)

  • 模板的处理,允许对tpl,html这类模板文件内嵌到 js 模块中
  • css的处理,允许样式内嵌到 js 模块中

合并输出(output)

在这个阶段主要就是按照 output 的配置,对模块合并输出,具体合并规则参看 [[package.json-:-output]]

打包 (build)

这个时候就是对模块进行些统一的一些收尾工作

  • min 模块压缩
  • install 安装到本地缓存
  • zip 是否打成 zip 包

上传到源中 (upload)

  • pack 我们会默认打包成 tar 包,然后进行上传
  • upload 会把 tar 包上传到我们配置的源中。

部署到指定地方 (deploy)

具体参看 [[spm deploy]]

上面看到的 resources, compile, analyse, preBuild, output, build 就是我们定义生命周期的各个阶段,而每个阶段中我们通过组合一系列插件来完成我们对应的工作。这样的一个好处就是通过这样的划分,我们可以很容易的把对应的任务产分到对应的地方。这样整个处理流程看起来就比较清晰.

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

SPM 0.8.x 使用说明

目前spm为了更好的对 CMD 规范下的模块更好的打包,目前在持续改进中。目前我们在arale项目中已经开始使用,后续我们会根据在使用过程中遇到的问题会不断的改进和调整。下面是目前做的一些改动

主要功能点

  1. spm build 根据配置文件package.json对项目打包。具体的过程可以看考 spm build 详解 #151
  2. spm server 启动源服务. 我们目前在项目打包阶段会对依赖的模块进行分析,目前通过这个服务,我们可以把打包的模块存储在此也可以从它里面读取需要的模块.
  3. spm search 模块查询. 输出模块名称可以查询模块的整体的信息.

安装说明

下载最新的spm代码

git clone https://github.com/seajs/spm.git
npm install spm/ -g

测试

spm -v // 0.8-dev

使用

使用很简单,就是到具体项目目录下执行

spm build     // 编译当前项目(依赖替换,文件合并,文件压缩),并把结果输出到当前项目下中的 dist目录

配置文件

在整个工具中需要使用到下面几类配置文件.

package.json

这个配置文件主要记录项目的一些描述信息. 这个主要存在下面两类位置

1. {projectDir}/package.json
这个是在具体项目中配置的一些信息,比如模块名称和版本,dependencies,output等信息,比如看我们widget项目的一个配置

{
    "version": "1.0",
    "name": 'widget',
    'output': {
        'widget.js': '.',
        'templatable.js': '.'
    },
    'dependencies': {
        'base': 'base/0.9.16/base' // 在这里可以覆盖arale默认的配置
    }

};

更多的配置选项参看https://github.com/seajs/spm/wiki/package.json.

2. .../(arale|alipay|cashier)/package.json
这个主要是配置我们整体项目的一些信息,比如dependencies等。子模块可以选择继承和覆盖此配置中的配置.

 // arale 
{
    dependencies: {
        // 外来模块
        '$': '$',

        …..
 }

config.json

这个主要配置spm打包的时候需要的一些全局信息, 现在配置的主要就是源服务的配置

1. ~/.spm/config.json 如果用户没有配置此文件,在第一次执行Build的时候会初始化一个默认的.

下面是具体的内容.很简单.

{
  sources: ['arale.alipay.im:8000']
}

额外说明

  1. 我们打包编译后所有模块的id都会被替换
define("#switchable/0.9.5/switchable-debug",
define("#switchable/0.9.5/switchable",
  1. 所有的require的全局模块会被配置的dependencies的信息进行替换。其中局部模块不会被替换. css和tpl会根据用户的配置选择是否进行代码内嵌.
    其中我们还支持保留关键字,比如我们在dependencies里面配置$: '$'表明这个依赖的模块需要通过在页面中进行解析,spm不会对他做任何处理.
    var $ = require("$");
    var Widget = require("#widget/0.9.16/widget-debug");
    var CONST = require("./const-debug");
    var Effects = require("./plugins/effects-debug");

  1. 对于模块依赖,我们会替换成全依赖,也就是会把依赖计算到最根部
["./const-debug", "./plugins/effects-debug", "./plugins/autoplay-debug", "./plugins/circular-debug", "./plugins/multiple-debug", "$-debug", "#widget/0.9.16/widget-debug", "#base/0.9.16/base-debug", "#events/0.9.1/events-debug", "#class/0.9.2/class-debug"],
  1. 对于arale以外的项目,我们会给模块的id加上额外的的信息,来区别生态圈中的模块。增加的内容就是我们在配置文件中的root: 'alipay'选项.
define("alipay/xbox/0.9.2/xbox-debug", [

详细命令

spm build -c 项目打包并使用google closure来进行代码压缩.

spm server -p 8001 -r alipay 把当前目录作为源服务的物理目录,并开启8001端口,并会增加额外的alipay(root) 目录保存这些模块.

风险点

  1. 如果js里面有中文的话,最好开启google closure。 默认的uglify压缩对中文处理有点问题.

常见错误

大家自己看下配置文件的规则。应该可以避免常见错误。

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

SPM 0.9.0 使用指南

简单、放心的包管理工具


安装

首先需要安装, node 和 npm: http://nodejs.org/#download

然后有两种安装方式:

###通过npm###

$ npm install spm -g

###通过源代码###

git clone https://github.com/seajs/spm.git
npm install spm/ -g

SPM 概要

目前我们的打包是基于配置文件,而且对模块的目录结构也有一定的要求,所以需要先了解下SPM打包模块下基本目录结构和一个经典的配置文件.

目录结构

package.json
README.md
src/
    example.js
dist/
    example-debug.js
    example.js
tests/
examples/

其中dist目录是我们打包好的模块. 也就是最终上线使用的模块.

配置文件(example)

{
    "name": "example",
    "version": "0.9.17",
    "dependencies": {
        "$": "$",
        "handlebars": "1.0.0",
        "base": "#base/0.9.16/base"
    },
    "output": {
        "name.js": ".",
        "name2.js": ['n1.js', 'n2.js']
    }
}

dependencies 相关依赖解析。

相关写法

  1. spm对于这种相等的模块,spm将不作处理,最终**$**的解析将有页面来决定.
  2. 这种是handlebars/1.0.0/handlebars的一种简化形式
  3. 是我们的标准配置.

依赖查找

由于打包的时候我们需要计算依赖关系,所以我们根据用户配置的依赖需要找到具体的模块,我们目前是通过源来完成的,对于一些标准模块
我们会提供统一的源服务,而对于用户的一些私有模块,我们也提供了命令可以方便用户快速的搭建自己的私有源服务.后面也会有相关的介绍.

outout 模块输出配置.

目前支持多种写法,最常用的就是上面两种:

  1. "." 会产生合并,其中会把name.js这个模块所有依赖的相对模块合并成一个模块name.js输出.
  2. 也是合并,不过是按照用户数组定义的顺序进行文件合并.

总之SPM 目前说简单点就是根据模块的配置文件,然后计算模块的依赖,并替换相关依赖,并把需要的文件合并起来,然后输出标准的CMD模块.

对于配置文件更详细的内容可以参看下面两个内容:

package, sources and spm

配置文件详情

对于具体的例子,可以参考我们已经开放出去的模块:

aralejs

SPM 相关命令

目前我们的命令大概可以分为两类.

模块打包

spm build [options]

根据package.json的配置打包模块并输出到dist目录:

$ spm build

其中有下面相关设置:

-c // 使用google closure compile 进行压缩

-X // 打印debug信息, 方便调试

spm upload [options]

打包模块(build),并把打包好后的dist目录的内容按照我们的定义上传到源服务中 方便其他人使用.

$ spm upload 

其中build的参数也都适用,有一个新增加的:

--only // 只进行上传,不会执行build操作.

spm deploy [options]

打包模块,并上传源服务,而且根据用户配置的远程服务器信息,可以把dist下面的内容scp到远程服务器.
具体的配置信息参看:

spm deploy 基本介绍

spm deploy 相关讨论

其中参数和upload的一致.

工具辅助

spm install [options] name[@Version]

获取所有的 seajs 兼容新模块到当前目录.

$ mkdir libs
$ cd libs
$ spm install all

也可以获取指定模块:

$ spm install jquery@1.7.2

查看更多详情:

$ spm help install

spm init

创建一个标准模块:

$ mkdir module
$ cd module
$ spm init

spm transport [--force] transport.js

你可以通过 transport 去包装一些非标准模块:

$ cd path/to/modules
$ mkdir xxx
$ cp jquery/transport.js xxx/
$ vi xxx/transport.js  # modify it
$ spm transport xxx/transport.js

spm server [options]

在当前目录启用源服务, 端口为 8000

$ spm server -p 8000

这样使用者可以在内网部署此服务,可以把模块部署到此服务,其他用户也可以从这个服务获取里面的模块.

如果一个服务想对多个系统(也就是模块配置有不同的root)提供服务的话

$ spm server -p 8001 --mapping

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

Spm build 之自定义目录结构

在上一篇文章中我们已经介绍了基于默认的目录结构的模块打包,其这篇文章主要针对任意目录结构的项目打包。下面涉及到的例子,用户可以根据自己项目的特点进行调整。

对于目录结构,SPM 本身没有太多的要求,只需要指定 SPM 需要操作的代码即可。

项目结构

在打包的过程中,由于会对代码本身的内容有改动(标准化,压缩,合并等),所以一般情况下会把代码分为两部分源码目录和部署上线的代码.
下面是一个简单的目录结构: 下面的例子来自社区用户,具体可以参看 #316

 ── assets # common modules and the third-party libaries
│   ├── backbone
│   │   └── 0.9.2
│   │       ├── backbone-min.js
│   │       └── backbone.js
│   ├── seajs
│   │   └── 1.2.1
│   │       ├── sea-min.js
│   │       └── sea.js
│   ├── ...
│   ├── plugin-tracker.js
│   ├── ... # common seajs plugins as plugin-tracker
│   └── css
│       └── reset.css
├── css
├── imgs
├── index.html
└── js  # a lot of app's custom modules
    └── app
         ├── bootstrap.js
         ├── models
         │   └── nearbydeals.js
         ├── template.js
         ├── views
         │   └── nearbydeals.js
         └── ...
     package.json

目录含义:

  • assets 目录下主要存放不经常变的通用模块、通用资源文件和第三方类库
  • js 下存放很多app相关自定义的业务、功能模块。其中app这个目录可以看做是一个大的模块项目,其中里面的js文件可以认为是子模块.

打包部署

首先我们需要提供下面几个信息, 其中下面的命令都是在js目录中执行.

必选

  1. 源文件
  2. 模块输出配置

这两个是必须提供的,其中我们提供了多种方式来让用户指定这两个参数:

源文件

spm deploy --src=app --to=../../

执行完这个命令后就会把app下面的代码进行简单的处理,部署到assets目录中了。不过由于缺少模块输出配置,所以目前执行的操作仅仅是代码压缩。
好,下面我们就看下合并输出配置

合并输出

合并输出规则很多,对于简单的规则我们可以通过命令行来完成:

spm deploy --src=app --output.bootstrap=.

这个命令的含义就是输出bootstrap这个模块,并会合并他内部的依赖模块.
由于命令行参数有限制,对于有些输出规则不太好通过命令行参数来描述,这样通过配置文件的方式是更好的选择。

配置加载

有两种方式可以指定配置文件的加载:

  • 在js目录中添加 package.json 文件。这样 SPM 在执行的时候就会默认进行加载
  • 也可以通过 --global-config 进行加载.
spm deploy --src=app --gobal-config=../config.json --to=../../

最终执行完,就会在assets中生成一个app目录,里面的模块就是已经处理好的模块了.

需要注意的是在这种情况下模块 ID 的生成是根据当前模块目录的位置来决定的,比如bootstrap.js他的 ID 就为 app/bootstrap.

版本控制

那有人问了,那我们是否也能像默认项目一样对我们的模块也提供版本控制呢?答案是可以的,只要提供相应的参数version即可。
version的添加也是一样,可以通过配置文件和命令行的参数.

spm deploy --src=app --global-config=../config.json --to=../../ --version=1.0.0

那么现在对应的bootstrap.js他的 ID 就为 app/1.0.0/bootstrap. 其中版本号也会在生成到 assets 中的目录中有体现.

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

SPM build 之默认目录结构

SPM 可以简单的认为就是一个项目打包工具 spm build,他的执行过程就是把我们在开发中的模块给规范化(CMD) ,其中还会进行依赖分析,并根据一些参数配置进行些优化操作,比如合并压缩等。这样以便更好的把代码部署到生产环境中.
下面我们还是来通过一个例子来说明如何把SPM应用到你的项目当中.
开始之前首先要安装SPM了,如何安装参看 安装指南

目前 SPM 有推荐的目录结构配置,但是用户也可以使用任意的目录结构,不过在打包的时候需要通过参数来提供打包需要的信息. 下面的演示我们主要是针对默认的目录结构的.

现在有两个模块,一个组件模块Xbox, 一个业务模块App, 下面是相关目录结构。

首先一个标准的模块有3部分组成:

  1. package.json 模块描述信息
  2. src 源文件.
  3. dist 打包后的文件, 里面包含了压缩的和未压缩(-debug)的两份文件. 需要注意的是需要把dist的代码部署到和id相关的目录,才能被正常访问,后面有具体演示.
# 模块xbox目录结构
xbox
  src/
    xbox.js
  dist/
    xbox.js
    xbox-debug.js
  package.json

相关代码如下:

# package.json
{
  "name": "xbox",
  "root": "",
  "version": "0.0.1",
  "dependencies": {
    "base": "1.0.0"
  },
  "output": {
    "xbox.js": "."
  }
}

# xbox.js
define(function(require, exports) {
  exports.show = function() {
    alert('hi, Im a xbox!');
  };
});

# dist/xbox-debug.js
define("xbox/0.0.1/xbox-debug", [], function(require, exports) {
  exports.show = function() {
    alert('hi, Im a xbox!');
  };
});

# dist/xbox.js
define("xbox/0.0.1/xbox",[],function(e,t){t.show=function(){alert("hi, Im a xbox!")}});

app 模块相关代码和目录结构.

app/
  src/
    app.js
  dist/
    app-debug.js
    app.js
  pakcage.json

# package.json
{
  "name": "app",
  "root": "",
  "version": "0.0.1",
  "dependencies": {
    "xbox": "0.0.1"
  },
  "output": {
    "app.js": "."
  }
}

# src/app.js
define(function(require, exports) {
  var xbox = require('xbox');
  exports.show = function() {
    xbox.show();
  };
});

# dist/app-debug.js
define("app/0.0.1/app-debug", ["xbox/0.0.1/xbox-debug"], function(require, exports) {
  var xbox = require('xbox/0.0.1/xbox-debug');
  exports.show = function() {
    xbox.show();
  };
});

#dist/app.js
define("app/0.0.1/app",["xbox/0.0.1/xbox"],function(e,t){var n=e("xbox/0.0.1/xbox");t.show=function(){n.show()}});

整体的目录结构,用于演示,用户可以根据自己的情况去调整.

sources
  app/  # 参看上面目录
  xbox/ # 参看上面目录
public/
  index.html
  seajs/
    1.2.1/
      sea.js
      sea-debug.js
  jquery/
    1.7.2/
      jquery.js
  app/
    0.0.1/
      app.js
      app-debug.js
  xbox
    0.0.1/
      xbox.js
      xbox-debug.js

下面是相关目录和文件说明:

index.html

模拟首页,其中里面调用了app组件

<html>
  <head>
      <meta http-equiv="content-type" content="text/html; charset=utf-8">
      <title>spm examples</title>
       <script type="text/javascript" charset="utf-8"  src="./seajs/1.2.1/sea.js"></script>
       <script type="text/javascript" charset="utf-8">
           seajs.config({
               alias: {
                   "$": "jquery/1.7.2/jquery"
               } 
           });
       </script>
       <script type="text/javascript" charset="utf-8">
           seajs.use(['$', 'app/0.0.1/app'], function($, app) {
                $('#test').on('click', function() {
                    app.show();     
                });
           });
       </script>
  </head>
  <body>
      <input id="test" type="button" value="click me"/>
  </body>
</html>

其中需要说明的就是app代码的加载路径,可以参看seajs/seajs#258 中中间关于base路径的说明。
不过最好还是仔细把seajs docs看下。

seajs, jquery

这两部分可以通过在public目录执行下面命令:

spm install seajs
spm install jquery

spm 会从默认源中去下载最新的seajs和jquery代码到本地目录。

app, xbox

默认我们执行spm build打包后,代码只会存在dist目录,而且这个代码需要被复制到对应的目录才能被页面访问和执行。
目前我们的spm deploy命令可以帮助把我们的模块部署到对应的目录,其中支持本地和远程部署,其中远程部署会有单独的文档来说明,在这里先看本地部署。我们在app和xbox目录可以分别执行:

spm deploy --to=../../public

执行这个命令后就会把打包后的文件部署到你指定的public目录中正确的模块可访问位置.

好了,整个就介绍完了?说了这么多,怎么可以快速验证呢?
我们可以到 public这个目录执行下面命令:

spm server

这个命令可以快速启动一个web服务,默认端口号是8000, 用户可以通过 -p 8002 来指定其他端口。
好了执行后就可以 访问 http://127.0.0.1:8000/index.html 来访问这个页面了.

这样一个基本的例子就介绍完了,其中文档涉及到的相关命令可以参看其他文档.

@afc163 afc163 changed the title spm@1.x 文档备份地址 spm@1.x 旧版文档备份 Jun 4, 2014
@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

SPM build 合并模块处理相关说明

spm build 打包支持模块的合并,目前对于合并模块的使用有些误解,在这里统一说明下。

为什么需要合并?

  • 代码的开发和维护
    在开发的时候,我们可以根据整体模块的特性,内部拆分成多个子模块,然后在开发完成后,可以合并成一个或多个模块对外提供服务,比如我们arale中的 widget
    他内部其实有4个模块,但是对外的只有2个模块:
widget/
    src/
       widget.js
       autoRender.js
       daparser.js
       templatable.js

# package.json
"output": {
    "widget.js": ".",
    "templatable.js": "."
}

这种情况其实可以认为就是一个外观模式,用户只需要关心widget提供的接口即可,具体daparser和autoRender在使用widget的时候是不需要关心的。

  • 优化模块页面请求
    在有些情况下我们可能希望减少页面的 http 请求次数,正常情况下不合并这些模块的话,那们这个模块加载完成后,还是会发起新的请求去加载这些外部模块的。这个时候我们就可以通过配置把外部模块也合并进来,比如 jquery, underscore这些都是可以的。
"output": {
    "module.js": "*"
}

这样打包出来的module.js里面将包含他的所有依赖,不过前提是在 module.js 的代码中对 jquery 这些模块有显示的依赖.

var $ = rquire('jquery');
var widget = require('widget/1.0.2/widget');

基本也可以归结为一个目的,减少页面请求.

有那些合并规则?

可以参看 [[package.json-:-output]]

合并模块的调用

其实如果我们对合并模块按照使用widget这种方式,其实是没有什么问题的。比如:

seajs.config({
  "alias": {
     "widget": "widget/1.0.2/widget"
   }
})

seajs.use('widget', function(Widget) {
    console.info(Widget);
)};

但是还有更多的情况下,我们对于合并模块的目的就是减少页面请求,这个时候我们我们可能有这种需求,比如加入有下面一个合并模块:

#test/1.0.0/module
define('#jquery/1.7.2/jquery', [], function(){...
define('#widget/1.0.2/widget', ["#base/1.0.1/base-debug", "#class/1.0.0/class-debug",...
define('#test/1.0.0/subModuleA', [], function(){...
define('#test/1.0.0/subModuleB', [], function(){...
define('#test/1.0.0/module', ['./subModule'], function(){...

在页面中正常使用module这个模块用法如下:

seajs.config({
    alias: {
        module: "#test/1.0.0/module"
    }
});

# 调用
seajs.use('module', function(mod){
   console.info(mod);  // 可以正确的拿到mod
});

但是, 其实有很多人还有一起需求,我们需要widet, 需要subModuleA怎么办呢? 有很多直觉的用法是

seajs.use(['widget/1.0.2/widget', 'test/1.0.0/subModuleA'], function(widget, a) {

});

如果这样执行的话,我们就会看到文件请求不到,因为如果通过id映射到的模块,线上并不存在,因为我们只是发布了 module 这个模块,widget和subModuleA是存在于module中的。
那么我通过map把widget, subModuleA都映射到 test/1.0.0/module 上行? 看运气,为什么?因为seajs有一个约定, 就是如果你请求的模块是合并模块,当请求模块的 url 和模块本身解析的出来的不一致。遇到这种情况时,seajs 会默认把第一个模块作为整个合并模块的主模块进行返回。
所以这种需求的前提就是必须先把module.js这个模块给加载下来,然后才可以访问他合并的模块,一般有两种方式

  • script 直接把合并的代码script到页面中,然后在其后面就可以随意使用seajs.use
  • 推荐做法
seajs.use('module', function(){
  seajs.use(['test/1.0.0/subModuleA', 'widget/1.0.2/widget'], function(subModuleA, Widget) {
      // 可以访问subModuleA 和 Widget 了.
  });
});

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

SPM build 命令行参数详解

如果存在下面目录结构

// 目录结构
app/
  deploy/
  public/
    contact
      model
        m.js
      tmpl
        t.tpl
    core
      js
        util.js
        config.js
    config
      seajs.config
    main.js

文件相关内容如下:

// main.js

define(function(require, exports, module) {
  var m = require('./contact/model/m.js');
  var $ = require('$');
  var base = require('base');

  exports.say = function(id) {
    m.say();
    console.info($(id));
    console.info(base);
  };
});

// m.js
define(function(require, exports, module) {
  var tpl = require('../tmpl/t.tpl');
  var config = require('../../core/js/config.js'); 
  var utils = require('../../core/js/utils.js'); 

  exports.say = function() {
    utils.log('tpl----->' + tpl);
    utils.log('config--->', config);
  };
});

// utils.js
define(function(require, exports) {
    exports.log = function(str) {
        console.info('[log] ' + str);
    };
});

//t.tpl
<div>hello</div>

//seajs.config
seajs.config({
  alias: {
    $: 'jquery/1.7.2/jquery',
    base: '1.0.0'
  }
});

那么我们目前可以基于下面命令进行打包合并。首先我们看下我们的最基本的需求:

  • 打包项目并部署到 deploy 目录中。
    首先进入到app目录,然后执行下面命令:
spm deploy --src=public --output.main=. --global-config=public/config/seajs.config --to=deploy

打包后的结果如下

// 目录结构
app/
  deploy/
    public/
      main.js
      main-debug.js


// main-debug.js
define("public/core/js/config-debug", [], {
  name: 'config',
  version: '1.0'
});


define("public/core/js/utils-debug", [], function(require, exports) {
    exports.log = function(str) {
        console.info('[log] ' + str);
    };
});


define("public/contact/model/m-debug", ["../../core/js/config-debug", "../../core/js/utils-debug"], function(require, exports, module) {
  var tpl = '<div>hello</div>';
  var config = require('../../core/js/config-debug'); 
  var utils = require('../../core/js/utils-debug'); 

  exports.say = function() {
    utils.log('tpl----->' + tpl);
    utils.log('config--->', config);
  };
});


define("public/main-debug", ["./core/js/config-debug", "./core/js/utils-debug", "./contact/model/m-debug", "#jquery/1.7.2/jquery-debug", "#base/1.0.0/base-debug", "#class/1.0.0/class-debug", "#events/1.0.0/events-debug"], function(require, exports, module) {
  var m = require('./contact/model/m-debug');
  var $ = require('#jquery/1.7.2/jquery-debug');
  var base = require('#base/1.0.0/base-debug');

  exports.say = function(id) {
    m.say();
    console.info($(id));
    console.info(base);
  };
});

好了,下面就解释下上面命令涉及到的参数.

  • src 指定打包的原文件目录
  • output 指定模块输出规则, 目前的含义就是输出 main模块,并合并内部依赖。
  • global-config 配置加载,可以加载一些配置被项目使用,具体的配置内容可以参看其他文档,不过为了方便用户我们也可以加载seajs.config内容,其中主要是会把alias和dependencies进行了映射.
  • to 指定部署的目录,会把打包好的模块,按照id生成到指定的目录中去的。

其中还有几个参数需要说明下:

  • root 模块的namespace,比如用户可以对整体的模块进行分类. 用户可以通过--root=js的形式来指定.
  • name 由于我们执行打包的模块必须有一个名字,所以当没有发现name的配置时,如果配置了src会以src的内容作为name也就是相当于模块文件的根目录. 用户可以通过--name=module 来指定
  • version 目前我们对于所有的模块都约定会有一个版本,用户可以通过--version=1.0.0来进行指定,这样会在模块id的中有体现,但是用户也可以选择无版本状态,这样生成模块id就会是模块名称+模块文件的具体位置.
  • dist 打包后输出的位置. 默认执行目录下面的dist目录,用户可以通过--dist=dist进行指定. 需要注意的是打包后的文件是直接被存放到根目录中的,这样的文件是不能直接被seajs进行使用的,如果要使用seajs进行加载还需要把模块部署到和id进行对应的目录中去.
  • source 可以指定本地源,用于读取依赖的模块信息. 这个参数和 deploy 的 to 有类似之处,在执行命令完成后,都会按照模块的id生成标准的模块内容.

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

SPM build 详解

spm build

spm build 的主要责任:将 src 文件根据用户配置的 pakcage.json 生成信息完备的符合 CMD 规范的模块到dist目录。

模块分为两种:

  1. 生态圈模块
  2. 私有源模块

下面只讨论生态圈模块, 私有源模块和生态圈模块类似,只是多了个root的处理.

输入与输出

假设有一个模块:

ninja/
    package.json
    src/
        a.js
        a-aio.js
        b.js
        c.js
        d.js

package.json 信息: [[package.json]]

{
    "name": "ninja",
    "version": "1.0.0",
    "dependencies": {
        "overlay": "1.2.1",
        "jquery": "1.7.2"
    },
    "output": {
        "a.js": ".", // 相当于把 a 的所有相对依赖打包成一个文件
        "a-aio.js": "*",  // 相当于把所有依赖模块打包成一个文件
        "d.js": ["d.js"]
    },
    "sources": ["http://modules.seajs.org"]
}

注: aio (all in one)

spm build 后的结果为:

ninja/
    package.json
    src/
        a.js
        a-aio.js
        b.js
        c.js
        d.js
    dist/
        a.js
        a-debug.js
        a-aio.js
        a-aio-debug.js
        d.js
        d-debug.js

其中 src 的内容为:

// a.js
define(function(require, exports, module) {
    var b = require('./b')
    var c = require('./c')
    ...
});

// a-aio.js
define(function(require, exports, module) {
    var b = require('./b')
    var c = require('./c')
    ...
});

// b.js
define(function(require, exports, module) {
    var overlay = require('overlay')
    // overlay 1.2.1 依赖 jquery 1.7.1
    ...
})
// c.js
define(function(require, exports, module) {
    var $ = require('jquery')
    ...
})
// d.js
define(function(require, exports, module) {
    ...
})

打包好的 dist 内容应该为:

a-debug.js:

define('#ninja/1.0.0/a', ['./b', './c', '#jquery/1.7.2/jquery', '#jquery/1.7.1/jquery', '#overlay/1.2.1/overlay'],
    function(require, exports, module) {
        var b = require('./b')
        var c = require('./c')
        ...
    }
)
define('#ninja/1.0.0/b', ['#jquery/1.7.1/jquery', '#overlay/1.2.1/overlay'],
    function(require, exports, module) {
        var overlay = require('#overlay/1.2.1/overlay')
    }
)
define('#ninja/1.0.0/c', ['#jquery/1.7.2/jquery'], function(require, exports, module) {
    var $ = require('#jquery/1.7.2/jquery')
})

a-aio-debug.js:

define('#ninja/1.0.0/a' …
define('#ninja/1.0.0/b' …
define('#ninja/1.0.0/c' …
define('#jquery/1.7.2/jquery' …
define('#jquery/1.7.1/jquery' …
define('#overlay/1.2.1/overlay' …

d-debug.js:

define('#ninja/1.0.0/d' …

id 的提取

$ spm build -v
>>> READING   package.json
>>> PROCESS OUTPUT   "a.js": "."
>>> GENERATE ID   #ninja/1.0.0/a

id = #name/version/filename

其中 name 与 version 从 package.json 中读取。

如果用户配置了root, 那么id规则为 id = root/name/version/filename

依赖提取

下面主要抽象的过程,实际日志可能有所不同.

>>> FIND DEPENDENCIES "a.js": ["./b", "./c"]
>>>
>>> PROCESS FILE ./b
>>> FOUND DEPENDENCIES ["overlay"]
>>> PARSE ALIAS "overlay" -> "#overlay/1.2.1/overlay"
>>> GET http://private-repo.alipay.com/overlay/1.2.1/overlay.tar.gz
>>> HTTP 404
>>> GET http://arale-repo.alipay.com/overlay/1.2.1/overlay.tar.gz
>>> HTTP 200
>>> READING overlay/1.2.1/dist/overlay-debug.js
>>> DEPENDENCIES ["#overlay/1.2.1/overlay", "#jquery/1.7.1/jquery"]
>>>
>>> PROCESS FILE ./c
>>> FOUND DEPENDENCIES ["jquery"]
>>> PARSE ALIAS "jquery" -> "#jquery/1.7.2/jquery"
>>> GET http://private-repo.alipay.com/jquery/1.7.2/jquery.tar.gz
>>> HTTP 404
>>> GET http://arale-repo.alipay.com/jquery/1.7.2/jquery.tar.gz
>>> HTTP 200
>>> READING jquery/1.7.2/dist/jquery-debug.js
>>> DEPENDENCIES ["#jquery/1.7.2/jquery"]
>>>
>>> DEPENDENCIES RESULT a.js: ["./b", "./c", "#overlay/1.2.1/overlay", "#jquery/1.7.1/jquery", "jquery/1.7.2/jquery"]

下载文件位于spm缓存中.

~/.spm/sources/modules.seajs.org-8000/
   overlay
        1.2.1/
        overlay-debug.js
   jquery
        1.7.1
          jquery-debug.js
          jquery.js
        1.7.2
          jquery-debug.js
          jquery.js

关于从 cache 读取依赖,另外再讨论。

require 替换

>>> MODIFY a.js
>>> REPLACE ./b -> ./b
>>> REPLACE ./c -> ./c
>>> MODIFY b.js
>>> REPLACE overlay -> #overlay/1.2.1/overlay
>>> MODIFY c.js
>>> REPLACE jquery -> #jquery/1.7.2/jquery

文件合并

>>> BUILD a.js
>>> CONCAT a.js b.js c.js -> dist/a-debug.js
>>> COMPRESS a-debug.js -> a.js
>>>
>>>
>>> PROCESS OUTPUT "a-aio.js": "*"
...
...
>>> FINISHED in 5s

部署

由于打包好的模块会生成id, 而我们在进行使用的时候会把请求的id对应为相关目录,所以在部署到的时候需要把dist下面的文件部署到和id对应的目录中.

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

Spm default lifycycle

spm 在开始设计的时候就以便于扩展作为设计思想,而目前的插件体系主要是用于在项目打包过程中,目前在SPM核心中已经内置了以下插件,后续使用者也可以通过配置的方式来扩展插件集合. 目前插件按照项目的生命周期进行了分组,下面就是基本的信息。

js

module.exports = [{
    'clean': ['clean']
}, {
    'resources': ['resources'] // 代码输出到build目录.
}, {
    'compile': ['coffee', 'less', 'transport'] // 代码编译.
}, {
    'analyse': ['lint','dependencies', 'depCheck'] //依赖分析.
}, {
    'preBuild': ['tpl', 'css'] // 代码模块规范化.
}, {
    'output': ['output'] // 合并输出.
}, {
    'build': ['min', 'install', 'zip'] // 代码压缩和本地缓存.
}, {
    'upload': ['pack', 'upload'] // 代码上传源.
}, {
    'publish': []
}, {
    'deploy': ['deploy'] // 代码部署.
}];

css

module.exports = [{
    'clean': ['clean']
}, {
    'resources': ['resources'] // 代码输出到build目录.
}, {
    'compile': ['less'] // 代码编译.
}, {
    'analyse': ['lint'] //依赖分析.
}, {
    'output': ['output'] // 合并输出.
}, {
    'build': ['min', 'install', 'zip'] // 代码压缩和本地缓存.
}, {
    'upload': ['pack', 'upload'] // 代码上传源.
}, {
    'publish': []
},{
    'deploy': ['deploy'] // 代码部署.
}];

package

module.exports = [{
  'build': ['build_all']
}, {
  'upload': ['build_all']
}, {
  'deploy': ['build_all']
}];

template

module.exports = [{
  'output': ['output_all']
}, {
  'build': ['install']
}, {
  'upload': ['pack', 'upload']
}];

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

spm deploy

~v1.4.0

把模块打包产生的 dist 文件部署到指定的位置。目前支持本地部署和服务器部署.

spm build 只是把模块打包后,直接放到 dist 目录。而打包后的模块如果需要被使用,是需要部署到对应的目录中的,比如 arale/widget/1.0.0/widget.js

本地部署

spm deploy --to=./public
spm deploy --to=../public

执行上面命令就会把模块部署到对应的目录中。

服务器部署

我们目前的服务器部署仅支持 ssh的方式。如果需要使用此方式,需要在 ~/.spm/config.json 中添加下面配置。下面的配置基本覆盖了所有情况.

"deploy": {
   "default": {
       "path": "/home/admin/wwwroot/assets/",
       "host": "assets.dev.example.com",
       "user": "admin",
       "pass": "alipaydev",
       "port": "22"
   },
   "h11":{
       "host": "assets.h11.example.com"
   },
   "h22":{
       "path": "/home/admin/wwwroot/assets/static/",
       "host": "assets.h22.example.com"
   },
   "local":{
       "host": "local",
       "path": "../projects/sea-modules"
   },
   "abc": {
       "host": "local",
       "path": "./abc"
   }
}

其中default, h11, h22, local 表示四个部署模块,h11 和 h22 中不全的信息会到 default 中补全。也可以自己写全。local 默认是本地部署,所以不会从 default 中进行信息补全.

spm deploy
spm deploy --to=h22
spm deploy --to=h11, h22
spm deploy --to=local
spm deploy --to=abc
spm deploy --to=def
spm deploy --to=./def
  • 第一种: 默认会使用 default 的配置
  • 第二种: h22 可以被匹配,所以只会部署 h22 的配置
  • 第三种: 我们可以支持同时部署2台服务器
  • 第四种, 第五种: 如果本地模块也经常部署的话,我们也可以直接配置进来。我们可以就可以简化本地部署的输入。

如果模块的 host 的内容为 "local", 则为本地部署模块,本地部署的模块的配置才不会从 default 中进行信息补全。

  • 第六种: 由于无法匹配,会提示警告信息.
  • 第七种: 如果 to 的内容是 . / 开头,我们就默认为本地部署. 也就是说会部署到当前的 def 目录。

统配部署

为了简化一些部署配置,我们也支持部分的变量替换. 此功能是通过额外的插件完成的,具体的使用参看 https://github.com/spmjs/plugin-alipay-deploy

"deploy": {
    "{{hole}}": {
        "host": "assets.{{hole}}.example.com"
    }
}

当插件配置到 spm 中后,如果用户执行

spm deploy --to=p123

当 p123 无法匹配到 deploy 中的配置项时,会根据 {{hole}} 配置产生下面配置

"p123": {
    "host": "assets.p123.example.com"
}

然后进入正常的部署流程。

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

spm init

spm init 使用说明

目前 spm init 推荐使用 来保存模板信息。也就是说模板其实也是一个模块,也需要按照模块的基本结构来定义,然后部署。

模块项目目录结构

arale_template/
    src/
            src/
                {{name}}.js
        examples/
        tests/
                {{name}}-spec.js
        package.json
        dist/
        package.json            

主要目录结构和原有的模块开发是一样的,我们只需要把初始化的模板内容放到 src 中即可。

模板文件名也支持 {{}} 这种形式,但是这里面的变量必须也要存在于模板文件内容中。

用户交互

交互主要分为两部分

模块选择

  1. spm init 从用户配置的源中,去查找所有项目类型为 template 的模块项目
  2. 读取这些项目配置中的 name 和 description 列表展示供用户选择
  3. 如果用户选择的模板有多个版本,需要用户再次选择版本
  4. 分析用户选择的模板模块内容,找出需要用户进行完善的内容
  5. 如果用户配置的源中没有发现模板,那么会直接输出默认的模板.

    模块信息完善

  6. 从所有文件中分析出来 {{***}},的内容
  7. 这些信息以交互的形式,由用户进行填写
  8. 替换这些变量到模板文件中,并生成到当前目录

模板项目开发

模板项目初始化

  1. 把需要提供给用户的内容,放到 src 目录中
  2. 把需要用户进行信息完善的部分,通过 {{**}} 的形式放到模板文件中

模板部署

  1. spm upload

参考

https://github.com/aralejs/template-arale

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

spm install

#安装模块
目前支持从源和 github 仓库进行模块安装

从源安装

spm install seajs
spm install arale.base
spm install gallery.jquery

默认会安装最新的稳定版模块,如果需要安装正在开发中的没有发布的模块:

spm install arale.base --unstable

我们也可以指定需要安装模块的版本

spm install gallery.jquery@1.8.2
spm install seajs@1.3.0

我们也可以指定安装目录,默认是安装在当前目录下的 sea-modules 目录中。

spm install arale.base --to=../public

注意事项

  1. seajs 是一个特殊的模块,只有这一个模块没有 root, 其他任何模块都需要指定 root.
  2. spm 会自动检查模块的依赖进行安装。

从 github 仓库进行模块安装

spm install aralejs/widget
spm install aralejs/autocomplete/0.9.0

如果模块在仓库打有tag,我们也可以安装上面的格式,指定模块的版本。

注意事项

  1. 使用此方法,仓库中的模块必须是标准的 CMD 模块。
  2. 这种方式实际上只是把仓库中的 dist 目录中的文件,按照 package.json 提供的内容,安装到对应的目录中。

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

spm transport

目前 spm transport 是通过 编译 的方式,把我们需要 transport 的模块进行转换。目前 transport 项目就是一个标准的模块项目.

backbone/
    src/
        backbone.transport
    dist/
        backbone.js
        backbone-debug.js
    package.json

语法

package.json

按照标准的模块配置来,其中大部分内容可以从原始的项目中copy 过来。需要注意的就是 root 和 output 配置

"root": "gallery",
"output": {
  "backbone.js": "."
}

transport

直接看代码吧

define(function(require, exports) {

  var previousUnderscore = this._;
  var previousJQuery = this.jQuery;
  this._ = require('underscore');
  this.jQuery = require('$');

  // @include https://raw.github.com/documentcloud/backbone/{{version}}/backbone.js

  this._ = previousUnderscore;
  this.jQuery = previousJQuery;
});

很简单,目前主要就是通过 // @include 这个语法,把需要的代码加载进来。然后对应的 url 中可以配置变量,变量的内容会从 package.json 中读取。

包含压缩文件项目的 transprot @1.5.1 新增

有些第三方模块比如 jquery, async 已经包含了压缩文件,而且他们的压缩文件的生成有自己的定制规则,对于这种模块我们就没有必要在对他们进行压缩了,为了让 spm 识别这种模块,对于这种方式的模块书写方式有所不同.

define(function(require, exports, module) {
  // @srcUrl https://raw.github.com/caolan/async/v{{version}}/lib/async.js
  // @minUrl https://raw.github.com/caolan/async/v{{version}}/dist/async.min.js
});

上面的是 async 模块。我们通过 @srcUrl, @minurl 来标明这个模块自身已经包含压缩过的文件,这样我们 spm 创建压缩模块的时候就会从指定的 minUrl 来进行读取,而不是重新进行压缩.

使用

spm build
spm upload
spm deploy

就是按照标准的模块项目执行就行了。

静态资源输出

资源文件的支持,通过增强output来完成

"output": {
  "a.swf": "http://path/{{name}}/{{version}}/a.swf"
}

js, css 资源输出

目前可能还有 css 这些标准模块的文件进行支持,比如下面这种形式

"output": {
  "a.css": "http://path/{{name}}/{{version}}/a.css",
  "b.js": "http://path/{{name}}/{{version}}/b.js"
}

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

spm 压缩参数配置

目前压缩设置分为两部分:

压缩工具的选择

spm build --compressor=yui  // 使用 yuicompressor 
spm build --compressor=closure // 使用 google closure
spm build  // 使用 uglifyjs

压缩参数的配置

目前只支持 yui 和 uglifyjs 的参数配置

yui 的压缩

spm build --compressor=yui --compress-options=v,nomunge

具体参数配置可以参看 http://developer.yahoo.com/yui/compressor/

uglifyjs 压缩

spm build --compress-options=reserved,beautify

具体参数配置可以参看 https://github.com/mishoo/UglifyJS2

source_map 现在还暂不支持.

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

Spm 命令详解

SPM 主要有以下命令
主要分为两大类。

打包类

下面这个三个命令都是基于项目标准的配置文件进行打包,其中主要的行为是基于用户配置的package.json.
其中也支持几个命令行参数来改变默认打包的行为

spm build

项目打包. 支持默认项目打包和任意目录结构打包,执行的过程有所不同,具体详情参看:

spm upload

项目打包,并把打包好的内容,部署到用户指定的源中.

spm upload --only // 仅执行上传命令.

spm deploy

项目打包,并部署到源中和用户配置的服务器上.

spm deploy --local=dist // 支持本地模块的部署. dist可以是本地任意目录.

spm deploy  // 部署到远程服务器, 通过ssh 来完成,需要一些配置
## 相关配置
# 在package.json增加服务器路径等相关信息
"deploy": {
    "#": "arale",
    "server": "dev",
    "host": "assets.alipay.com",
    "path": "/home/dev/"
   }

# 在~/.spm/config.json中增加用户认证信息
 "servers": {
    "dev": {
        "user": "admin",
        "pass": "888888"
    }
  }

功能类

spm install

根据用户指定,把模块安装到本地. 如果模块依赖的其他模块,也会把依赖的模块下载到本地.

spm install jquery
spm install widget // 支持依赖下载.

spm search

查找指定模块信息. 主要显示当前模块的版本信息,和子模块内容.

spm info gallery.jquery
spm info arale.widget

spm transport

根据用户写的 transport文件,把一个非标准模块,转换为标准的CMD模块,相关transport的语法例子可以参看:
http://modules.seajs.org/jquery/transport.js
http://modules.seajs.org/underscore/transport.js

spm transport jquery/
spm transport jquery/transport.js
spm transprort **/*.js // 批处理,transport的js文件,必须包含 **transport**.

spm env

用户本地spm 环境清理,主要用来清除本地缓存.

spm env --clean

spm server

启动一个源服务,用来进行多用户协同开发.默认是在8000端口开启一个web服务.

spm search -p 8002  // 指定端口
spm server --proxy=modules.seajs.org // 指定代理.

工具类

目前有些功能相对比较独立的插件,可以单独执行

spm concat

合并文件, 目前此功能比较简单,就是简单的合并,和cat命令类似.

spm concat src/*.js --dest=all.js

spm concat a.js b.js --dest=ab.js

spm min

压缩指定目录的内容并输出到指定的目录

spm min src/*.js // 压缩src目录中的所有js, 并输出到dist目录
spm min src/**/*.js --dist=target // 压缩src目录中,并递归查询对应的子目录中的js文件,并输出到target目录

# 可以通过 dest启动压缩合并功能
spm min src/*.js --dest=all.js // 压缩src下面的所有文件,并合并产生all.js

# 压缩 ab.js 到当前目录的 ab-min.js
spm min ab.js --dest=ab-min.js --dist=./

spm lint

使用jshint检查指定目录的代码

spm lint src/*.js
spm lint src/**/*.js

辅助

spm sources

对源的内容重新扫描,并生成相应的索引信息。

modules/
    gallery/
        jquery/
            1.8.2/
            1.8.3/
             info.json
        underscore/
             1.4.2/
             info.json
         info.json
    arale/
        widget/
            1.0.2/
            info.json
        info.json
    info.json

使用命令:

全部是在 modules 目录下面执行. 如果在内部执行,会产生副作用。所以需要注意.

spm sources  // 会更新全部模块信息。
spm sources gallery // 只更新 gallery 下面模块信息
spm sources arale/widget // 更新 arale 下面的 widget 信息
spm sources gallery/jquery/1.8.2 // 只更新 jquery/1.8.2/ 的模块信息

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

SPM 源详解

模块源是一个中央仓库,里面存放 tgz 格式的模块。它主要来共享模块,有了源只要用户提供模块的名称和版本,我们就能找到对应的模块了.
其中http://modules.spmjs.org 是我们默认提供的源服务,里面目前存放了目前比较成熟的 CMD 模块。 用户也可以根据我们提供的
spm server命令创建自己的源服务.

源的目录结构:

info.json             <--- 记录所有模块的信息
moment/
    moment.tgz
    info.json            <--- 该模块最近更新的时间,总共有哪些版本
    1.1.2/
        moment.tgz
        moment.js
        monment-debug.js
        src/
        package.json
jquery/
    jquery.tgz
    info.json
    1.7.2/
        jquery.tgz
        jquery.js
        jquery-debug.js
        package.json

源主要用于下面几个地方

spm build

在用户打包中,如果依赖了相关模块,spm会从源中找到这些模块,并分析出相关依赖,添加到当前模块信息中,这样就可以在用户加载js的时候,避免反复的去请求依赖了,因为我们在打包过程中,已经计算好了全部依赖了.

spm install

我们可以从源下载相关模块到本地.

$ spm install gallery.moment
GET http://modules.spmjs.org/moment/1.2.1/moment.tgz
// spm会根据源中的info.json计算出最新的版本进行下载.

$ spm install gallery.moment@1.1.1
GET http://modules.spmjs.org/moment/1.1.1/moment.zip

spm upload

当我们开发好一个模块,可以对外进行使用了,我们就可以使用这个命令把模块部署到源中(仅限于私有源,modules.spmjs.org这个源不能自动部署)
源服务会根据上传模块的信息把模块部署到正确的位置,并更新相关info.json信息.

搭建自己的源服务

这个其实很简单,找一个空目录执行

spm server -p 8000 --proxy=modules.spmjs.org

其中:

  • -p 指定端口号,默认就是8000, 如果使用默认可以不使用此参数
  • --proxy 有时候我们有多个源,而有些模块信息存放在其他源中,我们可以通过这个参数进行代理,当在本源中请求不到的时候,会从代理的源中去查找. 在上面我们执行了我们的中央源.

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

Spm 配置文件类型

SPM 在操作过程中会涉及到下面几类配置文件, 然后会根据用户执行命令的不同加载一个或多个配置文件,并按照顺序把内容合并起来,其中先入为主。
具体的合并规则如下:

  1. 基本类型,后来的不会覆盖先加载的。
  2. 数组,后来的会依次添加到后面。
  3. 对象. 会进行内容合并,内容的规则参看 1.

其中还需要注意的是,以json为后缀的配置文件,一定要严格遵守JSON的语法,否则不能继续后续的打包操作.

下面就是我们常见的几类配置文件. 下面列出的属性都是和SPM 的一些操作相关的,具体的可以参看package.json里面的内容,下面是具体的配置样板.

标准项目配置文件 - [[package.json]]

{
    "name": "example",
    "parent": "../config.json",
    "version": "1.0.0",
    "dependencies": {
        "$": "$",
        "handlebars": "1.0.0",
        "base": "#base/0.9.16/base"
    },
    "output": {
        "*": {
            "excludes": ["#jquery/1.7.2/jquery", "n1.js"]
         },
        "name1.js": ".",
        "name2.js": ["n1.js", "n2.js"],
        "name3.js": {
             "includes": "*",
             "excludes": ["n1.js", "n2.js"]
        },
        "sites": "sites/*.js"
    }
}

parent项目配置

属性规则和上面的package.json一致.

对于一个项目下面的模块可以抽象出来一部分公用配置. 比如我们可以配置root, sources这类公用信息.

{
    "root": "alipay",
    "sources": ["arale.alipay.im:8000"]
}

系统配置

属性规则和上面的package.json一致.

对于当前用户所有的项目生效,放置在 ~/.spm/config.json .目前主要用来存放默认源。
在spm 打包过程中,如果没有发现此文件,会自动初始化一个默认的.

{
    "root": "#",
    "sources": ["modules.seajs.org"]
}

源配置

我们还可以对使用同一源的模块设置相关配置,其实这个也可以看作是parent的一种扩展,区别就是一个在本地,一个在源上,这个源配置的主要用处在于配置一些涉及很多模块的,属性比较稳定的内容,而且配置改动后相关模块都会被应用到.
一般放置类似下面的位置.
http://arale.alipay.im:8000/alipay/config.json

属性规则和上面的package.json一致.

{
 "root": "alipay",
 "servers": {
    "dev": {
       "user": "dev",
       "pass": "dev123"
    }
  },
  "deploy": {
    "#": "arale",
    "server": "dev",
    "host": "assets.alipay.com",
    "path": "/home/dev/static/dev/static/"
   }
}

在我们打包过程中的加载顺序如下:

  1. 首先加载项目配置文件.
  2. 如果有parent配置,尝试加载parent配置.
  3. 如果用户配置 global-config 那么加载此配置.
  4. 加载用户目录下面的config.json
  5. 最后会尝试加载源配置.

所以如果大家可以根据自己的情况,把相关内容放置到对应的配置文件中.

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

全局配置: ~ .spm config.json

SPM 配置文件位于 ~/.spm/config.json (~表示操作系统的用户目录)

{
  "root": "arale",
  "sources": ["modules.spmjs.org"]
}

配置

  • root: 模块的根目录名。如果一个模块 detect 设置了 root 为 handy,那么打包之后模块的 id 是: handy/detect/1.0.0/detect.js 。详细文档请查看 [[打包的id规则]]。
  • sources: 配置 ,可以指定多个。依赖的模块将从这些指定的源中进行查找。默认为 http://modules.spmjs.org

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

命令行参数

spm

系统功能。

spm [options]

options

  • -h --help : 打印帮助
  • -v --version : 打印版本号

spm build

构建 cmd 模块。

spm build [options]

options

  • -v --verbose : 打印调试信息。
  • --src : 设置需要处理的模块目录,默认是 src
  • --base : 可以指定任意目录执行打包程序。
  • --build-config: 指定打包的文件,默认是 package.json
  • --gobal-config : 指定额外配置文件的加载。
  • --source : 指定源服务,支持本地源和web源。
  • --version : 指定模块的版本。
  • --source-files : 可以指定需要处理的文件列表。
  • --throwErrorOnDepNotFound : 如果依赖找不到的话,是否进行错误提示。
  • --with-debug : 生成 Debug 文件的标示。默认为 debug
  • --encoding : 指定在进行文件处理时,涉及到的文件编码。 默认为utf8
  • --output : 通过命令行指定模块输出规则。

examples

spm build // 默认执行打包程序。
spm build --src=app // 指定需要处理的模块目录。
spm build --global-config=../config.json // 加载额外的配置文件。
spm build --source=http://modules.seajs.org // 指定源服务。

spm upload

构建 cmd 模块。并上传到指定的源服务中。

spm upload [options]

options

spm build的参数对于spm upload都适用。

  • --only : 对当前目录处理好的模块文件执行 upload 操作,不会重新执行打包操作。

examples

spm upload --only

spm deploy

构建 cmd 模块。上传到指定的源服务中, 并把模块部署到指定的位置。

spm deploy [options]

options

spm build的参数对于spm upload都适用。

  • --to : 可以指定本地部署。

examples

spm deploy --to=../../public

spm install

安装指定的 cmd 模块到本地。

spm install [options]

options

  • --from : 指定从那里获取需要安装的模块,默认从用户配置的源中安装。
  • -f --force : 如果发现已经安装需要的模块,是否强制覆盖。
  • --to : 可以指定模块安装的目录。
  • -v --verbose : 打印调试信息。
  • --encoding : 指定文件处理编码,默认utf8

examples

spm install jquery
spm install widget // 支持依赖下载.
spm install jquery@1.7.2
spm install arale.widget@1.0.2 // {{root}}.{{name}}@{{verison}}

spm env

对 spm 本地环境进行清理。

spm env [options]

options

  • --clean : 清除本地缓存。

examples

spm env --clean

examples

spm info jquery
spm info widget
spm info alipay.xbox

spm server

启动源服务

spm server [options]

options

  • -p --port : 指定 web 服务端口
  • --proxy : 给此服务指定代理付服务,当模块在当前源中没有找到的话,回去代理服务中查询。
  • -v --verbose : 打印调试信息。
  • --encoding : 指定文件处理编码,默认utf8

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

在 Node 中调用

目前SPM的核心功能已经支持nodejs的接口调用,下面是相关使用指南.

spm build

模块打包,这个其实和命令行调用spm build比较类似,不过是通过显示的参数传递.具体用法和主要参数如下:

var build = require('spm').getAction('build');
var options = {
  base: '/projects/widget'
};

build.run(options, function() {
  console.info('success');
});

具体 options 支持下面参数:

标准项目

下面的参数主要用于标准的模块项目打包

base (必选)

设置模块打包目录,对于标准模块,一般情况下只需要设置这一个参数就够了。

baseModInfo

这个是设置模块基本信息,也就是说和 package.json 中对应的信息都可以通过这个配置进来。比如:

var options = {
   baseModInfo: {
       dependencies: {
           "$": "$",
           "overlay": "arale/overlay/0.9.12/overlay"
       },
       name: 'home',
       output: {"a.js": "."},
       root: 'local',
       version: '1.0.0'
   }
};

非标准项目

下面可以支持自由的目录结构,只要设置对应的参数,即可打包。

src

设置模块源文件目录,主要用于非标准目录结构. 用户可以通过这个参数指定需要打包模块的目录 /projects/app.
这个的优先级会高于 base 的设置.

/
 projects/
    app/
      moduleA.js
      moduleB.js

name

设置模块名称,主要用于模块的 id 生成。

version

设置模块版本,会体现在模块 id 中.

output

设置打包输出规则,具体参看 SPM 配置详解之output篇

通用配置

下面这些参数适用于任意情况

dist

设置打包后的模块部署的位置

with-debug

用于设置生成debug文件的名称规则, 默认是 'debug', 用户如果不需要产生debug文件可以设置为'' or false

source

用户指定模块的源

spm upload

spm build类似

spm deploy

spm build类似

spm install

用户把源中的模块快捷的安装到本地,支持依赖下载, 具体用法如下:

var install = require('spm').getAction('install');
var options = {
    modules: ['gallery.jquery', 'arale.widget'],
    to: '/projects/temp'
};

install.run(options, function() {
    console.info('install success!');
});

具体 options 支持下面参数:

modules

需要install的模块名称, 会从源中查找对应的内容,一般有下面两种形式

  1. gallery.jquery
  2. gallery.jquery@1.7.2

force

强制更新

to

安装到那里

from

从那里获取模块信息,主要是设置源地址.

spm init

初始化一个空项目.

var init = require('spm').getAction('init');
var options = {
    projectName: 'projectName', 
    base: '/project/modules'
};

init.run(options, function() {
    console.info('init success!');
});

具体 options 支持下面参数:

base

项目创建的目录地址.

projectName

项目名称,可选,如果没有设置的话,根据base的目录来作为项目名称, 比如 base为/projects/module 那项目名称为 module

spm env

var env = require('spm').getAction('env');
var options = {
   clean: true,
   init: 'http://modules.seajs.org'
};

env.run(options, function() {
    console.info('install success!');
});

具体 options 支持下面参数:

clean

清除本地缓存,主要就是删除 ~/.spm/sources下面的内容

init

从源中重新构建 ~/.spm/config.json 配置文件。

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

如何开发一个 spm 插件

这里说的插件主要是针对外部扩展的. spm 目前的插件机制是很简单的,开发一个插件只需要下面几步:

创建一个 spm 项目

插件的部署和打包本身也是通过 spm 来完成的,和普通的 CMD 模块使用的规则上是一样的

插件需要简单遵循一定规范

var plugin = module.exports = Plugin.create('json');

plugin.run = function(project, callback) {
  callback();
}

其实上面这段话就构成了一个最基本的插件。在 spm 中整个插件的执行都是串行的,目前还不支持并行执行.现在只需要在 run 函数里面添加你所需要执行的代码就行了。其中 project 这个对象里面提供了整个的项目信息,所以通过这个对象基本上可以完成大部分的操作。

TODO project 接口详解

内置了很多使用的模块

为了方便插件的开发,spm 在调用插件的时候会给插件设置很多全局变量,插件可以直接使用。目前有

  • Plugin 插件创建
  • fsExt 文件操作辅助
  • moduleHelp 模块操作辅助
  • Ast ast 的一些辅助操作。
  • DepUtil 模块的简单的依赖分析辅助

还有一些就是 spm 依赖的模块,插件也可以直接 require 使用

  • underscore
    等等

代码示例

spm-alipay-deploy

spm-online-status

spm-json

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

如何搭建一个源服务

如何快速搭建本地源服务

由于 modules.spmjs.org 目前访问不是很稳定,使用者实际可以搭建自己的源服务。

源服务目前在持续完善中,以后会提供更好,功能更完善的源服务。

搭建自己的源服务

  • 找到一个空目录并在这个目录创建一个 config.json 文件
{
  "roots": ["arale", "gallery", "test"]
}

这个 roots 的是指这个源可以接收那些 root 的模块上传。

  • 如果希望提仓库提前安装一些模块,可以执行下面命令:
spm install arale.base --to=.
spm install gallery.jquery --to=.
spm install gallery --to=.  // 批处理安装
spm install arale --to=.    // 批处理安装

这个时候需要在 arale 下面和 gallery 目录下面创建 config.json 。默认是空的 {} 就行
这是因为只有这个文件,才能表明这个目录是 root 目录。

  • 初始化源信息
spm sources --stable   // 生成模块稳定版信息
spm sources  // 生成完成的源信息

通过上面的命令就可以把当前目录的模块,分析出来,并产生出 info.json 文件,这样spm 打包或者 install 的时候就可以使用这个源了。

  • 启动源服务
spm server -p 8000

这样就完成了一个源服务器的搭建了。

高级功能

代理

我们也只模块的代理,也就是说当用户从这个源服务请求的模块找不到的话,可以从代理的源服务中去查找.

  • 创建一个 package.json
{
  "proxy": ["http://modules.spmjs.org"]
}
  • 启动源服务
spm server -p 8000

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

安装

首先需要安装 node 和 npm: http://nodejs.org/#download

注意:新版 node 自带 npm,不需要独立安装

然后有两种安装方式:

通过 npm 安装

$ npm install spm -g

通过克隆 Git 上的源码安装

$ git clone https://github.com/spmjs/spm.git
$ cd spm
$ npm install . -g

运行

spm -V 

输出正确的版本,就表明安装没有问题了.

升级

{{怎么升级}}

删除

{{怎么删除}}

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

HOME

简单、放心的包管理工具 。

SPM 是一个基于命令行的前端项目管理工具,目前可以支持 JS 和 CSS 的项目。

注意:以下是 spm 1.x 版本的文档,已不再维护更新。SPM 2.0 已经发布,功能和调用方式发生了很大变化,文档请查看 http://docs.spmjs.org 。以下文档仅使用于 1.x.x 版本。

点此查看 [[安装]]。

使用教程

快速上手

由于 SPM 和 SeaJS 关系密切,我们推荐你直接查看 SeaJS 官网的 快速上手 教程。通过这篇教程,你将学会如何开发一个小型项目。

进阶教程

如果快速上手难不倒你,那就准备接受更大的挑战把:)

请查看 [[Hello spm:使用 spm 和 SeaJS 开发一个中型项目]],教程以开发步骤为顺序讲解。通过这部分内容,你将逐步了解到 SPM 绝大部分的常用功能,并能胜任实战中的项目开发。

API 文档

  • 配置[[package.json]]
    • output
    • spmConfig
    • [[全局配置: ~/.spm/config.json]]
    • [[Spm build 之自定义目录结构]]
  • [[命令行参数]]
  • [[spm transport]]
  • [[spm install]]
  • [[spm deploy]]
  • [[spm init]]
  • [[在 Node 中调用]]
  • [[spm--js-模块生命周期解析]]
  • 如何搭建一个源服务
  • [[SPM build 合并模块处理相关说明]]
  • [[如何开发一个 spm 插件]]
  • 版本变更日志

讨论和报告 Bug

@afc163
Copy link
Member Author

afc163 commented Jun 4, 2014

目录结构

标准模块的目录结构:

package.json
README.md
src/
    name.js
dist/
    name-debug.js
    name.js
tests/
examples/
docs/

其中 dist 是由 spm build 生成的。

一个标准模块的最小结构:

package.json
src/

@afc163 afc163 closed this as completed Jun 9, 2014
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests

1 participant