postcss-less2scss:将 Less 转换成 Scss 的 PostCSS 插件

发布一个 PostCSS 插件:postcss-less2scss,可以将 Less 文件转换为 Scss。
* GitHub:https://www.npmjs.com/package/postcss-less2scss
* NPM:https://www.npmjs.com/package/postcss-less2scss

转换变量

转换变量的定义和使用

  • 不属于任何一个 Rule 的变量

Less:

@link-color: #428bca;

Scss:

$link-color: #428bca;
  • 在某个 Rule 中定义的变量

Less:

#main {
  @width: 5em;
  width: @width;
}

Scss:

#main {
  $width: 5em;
  width: $width;
}
  • 在 Declaration 的 value 中使用的变量

Less:

@text-color: @gray-dark;
@link-color-hover:  darken(@link-color, 10%);

Scss:

$text-color: $gray-dark;
$link-color-hover:  darken($link-color, 10%);
  • 在某个 Rule 中的一个 Declaration 的 value 中使用的变量

Less:

a:hover {
  color: @link-color-hover;
}

Scss:

a:hover {
  color: $link-color-hover;
}
  • 转换 At-Rules 中的变量

Less:

@screen-sm:                  768px;
@screen-sm-min:              @screen-sm;

.form-inline {

  // Kick in the inline
  @media (min-width: @screen-sm-min) {
    // Inline-block all the things for "inline"
    .form-group {
      display: inline-block;
      margin-bottom: 0;
      vertical-align: middle;
    }
  }
}

Scss:

$screen-sm:                  768px;
$screen-sm-min:              $screen-sm;

.form-inline {

  // Kick in the inline
  @media (min-width: $screen-sm-min) {
    // Inline-block all the things for "inline"
    .form-group {
      display: inline-block;
      margin-bottom: 0;
      vertical-align: middle;
    }
  }
}

Variable Interpolation

  • 转换选择器中的 variable interpolation

Less:

// Variables
@my-selector: banner;

// Usage
.@{my-selector} {
  font-weight: bold;
  line-height: 40px;
  margin: 0 auto;
}

Scss:

// Variables
$my-selector: banner;

// Usage
.#{$my-selector} {
  font-weight: bold;
  line-height: 40px;
  margin: 0 auto;
}

转换 Mixins

  • 转换 Mixins 的定义(可以支持默认参数)

Less:

.alert-variant(@background; @border; @text-color) {
  background-color: @background;
  border-color: @border;
  color: @text-color;

  hr {
    border-top-color: darken(@border, 5%);
  }
  .alert-link {
    color: darken(@text-color, 10%);
  }
}

Scss:

@mixin alert-variant($background, $border, $text-color) {
  background-color: $background;
  border-color: $border;
  color: $text-color;

  hr {
    border-top-color: darken($border, 5%);
  }
  .alert-link {
    color: darken($text-color, 10%);
  }
}
  • 转换 Mixins 的使用

Less:

.a {
    .center-block;
}

Scss:

.a {
    @include center-block;
}
  • 支持 Mixins 的具有默认值的参数

Less:

@state-success-text:             #3c763d;
@state-success-bg:               #dff0d8;
@state-success-border:           darken(spin(@state-success-bg, -10), 5%);

@state-info-text:                #31708f;
@state-info-bg:                  #d9edf7;
@state-info-border:              darken(spin(@state-info-bg, -10), 7%);

@state-warning-text:             #8a6d3b;
@state-warning-bg:               #fcf8e3;
@state-warning-border:           darken(spin(@state-warning-bg, -10), 5%);

@state-danger-text:              #a94442;
@state-danger-bg:                #f2dede;
@state-danger-border:            darken(spin(@state-danger-bg, -10), 5%);

.box-shadow(@shadow) {
  -webkit-box-shadow: @shadow; // iOS <4.3 & Android <4.1
          box-shadow: @shadow;
}

.form-control-validation(@text-color: #555; @border-color: #ccc; @background-color: #f5f5f5) {
  // Color the label and help text
  .help-block,
  .control-label,
  .radio,
  .checkbox,
  .radio-inline,
  .checkbox-inline,
  &.radio label,
  &.checkbox label,
  &.radio-inline label,
  &.checkbox-inline label  {
    color: @text-color;
  }
  // Set the border and box shadow on specific inputs to match
  .form-control {
    border-color: @border-color;
    .box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
    &:focus {
      border-color: darken(@border-color, 10%);
      @shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten(@border-color, 20%);
      .box-shadow(@shadow);
    }
  }
  // Set validation states also for addons
  .input-group-addon {
    color: @text-color;
    border-color: @border-color;
    background-color: @background-color;
  }
  // Optional feedback icon
  .form-control-feedback {
    color: @text-color;
  }
}

// Feedback states
.has-success {
  .form-control-validation(@state-success-text; @state-success-text; @state-success-bg);
}
.has-warning {
  .form-control-validation(@state-warning-text; @state-warning-text; @state-warning-bg);
}
.has-error {
  .form-control-validation(@state-danger-text; @state-danger-text; @state-danger-bg);
}

Scss:

$state-success-text:             #3c763d;
$state-success-bg:               #dff0d8;
$state-success-border:           darken(adjust_hue($state-success-bg, -10), 5%);

$state-info-text:                #31708f;
$state-info-bg:                  #d9edf7;
$state-info-border:              darken(adjust_hue($state-info-bg, -10), 7%);

$state-warning-text:             #8a6d3b;
$state-warning-bg:               #fcf8e3;
$state-warning-border:           darken(adjust_hue($state-warning-bg, -10), 5%);

$state-danger-text:              #a94442;
$state-danger-bg:                #f2dede;
$state-danger-border:            darken(adjust_hue($state-danger-bg, -10), 5%);

@mixin box-shadow($shadow) {
  -webkit-box-shadow: $shadow; // iOS <4.3 & Android <4.1
          box-shadow: $shadow;
}

@mixin form-control-validation($text-color: #555, $border-color: #ccc, $background-color: #f5f5f5) {
  // Color the label and help text
  .help-block,
  .control-label,
  .radio,
  .checkbox,
  .radio-inline,
  .checkbox-inline,
  &.radio label,
  &.checkbox label,
  &.radio-inline label,
  &.checkbox-inline label  {
    color: $text-color;
  }
  // Set the border and box shadow on specific inputs to match
  .form-control {
    border-color: $border-color;
    @include box-shadow(inset 0 1px 1px rgba(0,0,0,.075)); // Redeclare so transitions work
    &:focus {
      border-color: darken($border-color, 10%);
      $shadow: inset 0 1px 1px rgba(0,0,0,.075), 0 0 6px lighten($border-color, 20%);
      @include box-shadow($shadow);
    }
  }
  // Set validation states also for addons
  .input-group-addon {
    color: $text-color;
    border-color: $border-color;
    background-color: $background-color;
  }
  // Optional feedback icon
  .form-control-feedback {
    color: $text-color;
  }
}

// Feedback states
.has-success {
  @include form-control-validation($state-success-text, $state-success-text, $state-success-bg);
}
.has-warning {
  @include form-control-validation($state-warning-text, $state-warning-text, $state-warning-bg);
}
.has-error {
  @include form-control-validation($state-danger-text, $state-danger-text, $state-danger-bg);
}

转换函数

字符串函数

  • 转换 CSS 转义函数,也就是:~”xxx”

Less:

@input-border-focus:             #66afe9;

.box-shadow(@shadow) {
  -webkit-box-shadow: @shadow; // iOS &lt;4.3 & Android &lt;4.1
  box-shadow: @shadow;
}

.form-control-focus(@color: @input-border-focus) {
  @color-rgba: rgba(red(@color), green(@color), blue(@color), .6);
  &:focus {
    border-color: @color;
    outline: 0;
    .box-shadow(~"inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px @{color-rgba}");
  }
}

.form-control {
  .form-control-focus();
}

Scss:

$input-border-focus:             #66afe9;

@mixin box-shadow($shadow) {
  -webkit-box-shadow: $shadow; // iOS &lt;4.3 & Android &lt;4.1
  box-shadow: $shadow;
}

@mixin form-control-focus($color: $input-border-focus) {
  $color-rgba: rgba(red($color), green($color), blue($color), .6);
  &:focus {
    border-color: $color;
    outline: 0;
    @include box-shadow(#{inset 0 1px 1px rgba(0,0,0,.075), 0 0 8px $color-rgba});
  }
}

.form-control {
  @include form-control-focus();
}

颜色函数

  • 将 Less 的 spin() 函数转换为 Scss 的 adjust_hue() 函数

Less:

@state-success-border:           darken(spin(@state-success-bg, -10), 5%);

Scss:

$state-success-border:           darken(adjust_hue($state-success-bg, -10), 5%);

转换 @import At-Rules

Less:

@import "foo";
@import "foo.less";
@import "foo.php";
@import "foo.css";

Scss:

@import "foo";
@import "foo";
@import "foo";
@import "foo.css";

如果使用 postcss-less2scss 插件

const postcss = require('postcss')
const syntax = require('postcss-less')
const converter = require('postcss-less2scss')

const lessText = `
// Variables
@link-color:        #428bca; // sea blue
@link-color-hover:  darken(@link-color, 10%);

// Usage
a,
.link {
  color: @link-color;
}
a:hover {
  color: @link-color-hover;
}
.widget {
  color: #fff;
  background: @link-color;
}
`
const scssText = postcss([converter])
  .process(lessText, { syntax })
  .css
console.log('lessText = ', lessText, ', \nscssText = ', scssText)

和 gulp 集成

/**
 * Use postcss-less2scss to convert bootstrap v3.3.7 styles from less to scss
 */
gulp.task('less2scss', () => {
  return gulp.src('assets/less/bootstrap3/**/*.less')
    .pipe(postcss([less2scss], {
      syntax: less
    }))
    .pipe(rename(path => {
      if (path.basename !== 'bootstrap') {
        path.basename = '_' + path.basename
      }
      path.extname = '.scss'
    }))
    .pipe(gulp.dest('build/scss/bootstrap3/'))
})

postcss-remove-global: 从代码中移除 :global 的 PostCSS 插件

postcss-remove-global

发布一个 PostCSS 插件,用来移除样式中的 :global 标识符。
* github 链接:https://github.com/princetoad/postcss-remove-global
* npm 链接:https://www.npmjs.com/package/postcss-remove-global

目前支持三种场景,第一种是 :global 作为一个单独的选择器,第二种是 :global 作为选择器的一部分,第三种是 :global 作为 @keyframe 属性的一部分。分别对应下面的三个示例。

示例

  1. Remove :global as a single selector
:global {
a { }
}
a { }
  1. Remove :global as part of a selecotr
.root :global .text { margin: 0 6px; }
.root .text { margin: 0 6px; }
  1. Remove :global as part of params of @keyframe
@keyframes :global(zoomIn) { }
@keyframes zoomIn { }

如何使用

使用 postcss API

const postcss = require('postcss')
const removeGlobal = require('postcss-remove-global')

const css = postcss()
.use(removeGlobal())
.process(':global { a {color: gray(85); background-color: gray(10%, .25)}}')
.css
console.log('css = ', css)
//= 'a {color: gray(85); background-color: gray(10%, .25)}'

const css2 = postcss([removeGlobal])
.process('.root :global .text { margin: 0 6px; }')
.css
console.log('css2 = ', css2)
//= '.root .text { margin: 0 6px; }'

const css3 = postcss([removeGlobal])
.process('@keyframes :global(zoomIn) { }')
.css
console.log('css3 = ', css3)
//= '@keyframes zoomIn { }'

参见:https://github.com/princetoad/try-postcss/blob/master/src/Plugin/plugin-remove-global.js

结合 gulp

gulp.task('global', () => {
return gulp.src('assets/*.css')
.pipe(postcss([removeGlobal]))
.pipe(gulp.dest('build/'))
})

参见:https://github.com/princetoad/try-postcss/blob/master/gulpfile.js

generator-criket: 一个 Node.js 命令行项目脚手架

一看 generator-criket 这个名字,有经验的人就知道这是基于 yeoman 的.
至于为什么命名为 criket (蛐蛐)? 则是因为我正在看王世襄先生的<京华忆往>,里面有讲到百灵( Lark) 和蛐蛐( criket). Lark 已经被人用了, 有一个 Lark.js 以及对应的 generator-lark, 我就只能用蛐蛐了.

蛐蛐的用法很简单, 装好 yeoman(npm install -g yo) 之后,直接运行 yo criket,然后按照提示一步一步输入相关信息即可,然后一个 Node.js 命令行项目就创建完成了.
我的 augustine,frege 等命令行工具都是基于这个脚手架的.

Frege – 由 package.json 逆向生成 npm install/yarn add 安装命令的工具

更新 v0.3: 使用 -l -u 或者 --latest --update 选项可以直接运行生成的最新版本的依赖包安装脚本, 将 package.json 以及 node_moduels下面的依赖包都更新至最新版本. Note: 生产项目慎用


更新 v0.2: 支持生成 yarn 安装脚本, 使用 -y 或者 --yarnInstall 参数


frege 是什么?

frege 可以从一个现有的 package.json 逆向生成安装所需的 npm install 脚本. 特点是可以选择仅生成生产环境或者开发环境包安装脚本, 并且能够正确将 version range 转成 npm install 所需的语法.

安装

建议把 frege 安装到全局, 然后就可以在命令下直接使用了.

npm install frege -g

使用

参数说明

frege [options]

基本配置:
-f, --file String 要解析的 package.json 文件, 默认会解析当前目录下名为 package.json 的文件 - default: package.json
-l, --latest 不管 package.json 中 npm 包的具体版本号, 安装该包的最新版本 - default: false
-p, --productionOnly 仅生成 dependencies 项目下 npm 包的安装脚本. - default: false
-d, --devOnly 仅生成 devDependencies 项目下的 npm 包的安装脚本, 即开发使用的. - default: false
-h, --help Show help
-v, --version Output the version number

示例

frege

frege

不带任何参数直接运行 frege 命令将生成当前目录下 package.json 文件中 dependenciesdevDependencis(如果有的话) 全部 npm 包的安装脚本, 例如:

npm i -S debug@">=2.6.8 <3.0.0" optionator@">=0.8.2 <0.9.0" semver@">=5.3.0 <6.0.0" npm i -D ava@">=0.19.1 <0.20.0" chai@">=4.0.1 <5.0.0" eslint@">=3.19.0 <4.0.0" tap-nyan@">=1.1.0 <2.0.0"

frege -p

frege --productionOnly

的缩写, 仅生成 dependencis 下面 npm 包的安装脚本.

npm i -S debug@">=2.6.8 <3.0.0" optionator@">=0.8.2 <0.9.0" semver@">=5.3.0 <6.0.0"

frege -d

frege --devOnly

的缩写, 仅生成 devDependencis 项下面 npm 包的安装脚本.

npm i -D ava@">=0.19.1 <0.20.0" chai@">=4.0.1 <5.0.0" eslint@">=3.19.0 <4.0.0" tap-nyan@">=1.1.0 <2.0.0"

frege -l

frege --latest

的简写, 安装 npm 包的最新版本, 而不是原有 package.json 中指定的版本范围.

npm i -S debug optionator semver
npm i -D ava chai eslint tap-nyan

frege -f ../augustine/package.json

指定要解析的 package.json 文件的完整路径.

有问题欢迎反馈!

github 地址: https://github.com/princetoad/frege
npm 地址: https://www.npmjs.com/package/frege

范圣刚 <tom@tfan.org>

Augustine – 一个简单的静态文件 HTTP Server

发布了一个简单的基于 node.js 的 HTTP Server, 可以用来 serve 静态资源, 作为纯前端项目的 Web 服务器.

特点: 简单, 简小, 可以通过 npm 安装.

功能:

  1. 支持 HTTP Status Code – 404 Not Found
  2. 如果路径是目录的话, 支持默认到 index.html 文件
  3. 支持 debug 信息(DEBUG=augustine)

使用 npm 安装

npm install augustine --save-dev

如何使用

在作为 Web 目录的文件夹下新建一个例如名为 index.js 文件, 加入下面两行代码:

const augustine = require('augustine');
augustine.start(8080);

然后运行

node index.js

即可通过 8080 端口访问该目录下的内容了.

欢迎参与

github 地址: https://github.com/princetoad/augustine
npm 地址: https://www.npmjs.com/package/augustine

演讲:Node.js 软件开发实践分享

2013年12月5日,我在 51CTO 举办的 『Cloud框架和 Node.js 技术沙龙』上进行了主题为「Node.js 软件开发实践分享」的演讲。

内容包括 Node.js 的模块系统,异常捕捉和错误处理同步操作简化和异步工作流控制,以及生产环境运行和日志这四个方面的主题,同时还介绍了我在 Node.js 开发过程中的一些有趣的技术小故事。

非常感谢 51CTO和百度提供的 Node.js 技术交流的机会,也非常感谢参会同学的积极参加和提问。

应有关参会同学的要求,把演讲 PPT 贴了出来,下面是 PDF 格式的链接:
Node.js 软件开发实践分享

12.11 更新:51CTO网站把这次活动放到头条了,并且放出了所有的 ppt 和演讲视频,地址是:http://mobile.51cto.com/exp/cloudasalon1206/

使用 Node.js 和 MongoDB 开发高性能微信公众平台应用

发布仅两年即坐拥三亿用户的微信无疑是当前国内最炙手可热的移动互联网应用,腾讯也据此稳稳拿到一张移动互联网的“船票”。微信公众平台的推出,则让其他企业和个人有机会通过微信渠道将业务和品牌推广给上亿的微信用户,可以大大地减少宣传成本;同时也可以充分利用微信公众平台接口的能力和移动终端特性,开发出创新的产品和商业模式。

使用微信公众平台我们既可以向订阅用户群发消息,也可以编程实现对用户发送的消息进行自动回复,同时还可以管理用户的订阅和取消事件等;支持的消息类型既包括文本,语音,图片,位置信息和链接,也可以是丰富的图文信息。而且未来肯定还会不断地增加新的功能,比如移动支付。

因此从本质上来看,微信公众平台就是一个豪华版的移动互联网时代的“梦网”,想想当年的“移动梦网”可是拯救了整个的国内互联网公司,微信公众平台的发展未来绝对可以用“不可限量”来形容。

这个 presentation 主要与大家交流微信公众平台应用开发的相关内容,涵盖接入,设计,开发,调试和测试等全流程,将会对微信公众平台接口和能力进行全面而详尽的剖析。

我们使用流行的 Node.js + MongoDB 的组合进行讲解和演示,最终使大家都能够开发出支持百万用户以上的高性能可伸缩的微信公众平台应用。

PPT

原文地址:http://www.tfan.org/wechat-on-nodejs-and-mongodb/

如何让 Node-express 支持 XML 形式的 POST 请求?

express 是基于 connect 开发的,使用 bodyParser 对请求的包体进行解析,默认支持:application/json, application/x-www-form-urlencoded, 以及 multipart/form-data。 也就是说不支持对 XML 形式的包体进行解析。

但是以 XML 格式作为接口数据交换还是有人在用,比如 Microsoft 的 Bing Translator HTTP API,以及腾讯微信的公众平台接口。之前用 Node.js 实现调用 Bing Translator 接口时用过 Node.js 的 xml2js 库,可以把 XML 转换成 JSON 格式以方便我们的程序进行解析。这里我们同样可以使用 xml2js 扩展 express 以使其支持 XML 形式的请求。

参考:

  • https://gist.github.com/davidkrisch/2210498
  • http://expressjs.com/api.html#bodyParser
  • http://stackoverflow.com/questions/11002046/extracting-post-data-with-express
  • https://groups.google.com/forum/?fromgroups=#!topic/express-js/6zAebaDY6ug

在 CentOS 上使用 Apache 的 Proxy mode 使 Node.js 和 Apache 共用 80 端口

最近用 Node.js 开发一个微信公共平台接口,由于微信公众平台接口配置目前仅支持默认的 80 端口,而我的 CentOS 服务器已经在使用 Apache 运行一些其他业务了。这样就必须要求 Node.js 能够和 Apache 共用 80 端口。

我们可以使用 Apache 的 Proxy mode 来解决这个问题。

(更多…)