Skip to content

Commit

Permalink
feat(stylus): 添加stylus支持 (#3483)
Browse files Browse the repository at this point in the history
* feat(stylus): 添加stylus支持

修改了两个模块 af-webpack 和 umi-build-dev

* feat(unit test): 增加单元测试,根据建议调整代码

主要修改af-webpack模块
  • Loading branch information
fengxinming authored and sorrycc committed Oct 31, 2019
1 parent 7de9c97 commit b65a74e
Show file tree
Hide file tree
Showing 10 changed files with 200 additions and 6 deletions.
4 changes: 3 additions & 1 deletion packages/af-webpack/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -89,7 +89,9 @@
},
"devDependencies": {
"node-sass": "4.12.0",
"sass-loader": "7.1.0"
"sass-loader": "7.1.0",
"stylus": "^0.54.7",
"stylus-loader": "^3.0.2"
},
"repository": {
"type": "git",
Expand Down
11 changes: 11 additions & 0 deletions packages/af-webpack/src/getUserConfig/configs/stylus.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import assert from 'assert';
import { isPlainObject } from 'lodash';

export default function() {
return {
name: 'stylus',
validate(val) {
assert(isPlainObject(val), `The stylus config must be Plain Object, but got ${val}`);
},
};
}
59 changes: 57 additions & 2 deletions packages/af-webpack/src/getWebpackConfig/css.js
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export default function(webpackConfig, opts) {
hasSassLoader = false;
}

function applyCSSRules(rule, { cssModules, less, sass }) {
function applyCSSRules(rule, { cssModules, less, sass, stylus }) {
if (!opts.ssr) {
if (opts.styleLoader) {
rule
Expand Down Expand Up @@ -130,6 +130,35 @@ export default function(webpackConfig, opts) {
.loader(require.resolve('sass-loader'))
.options(opts.sass);
}

// 开始添加 stylus 支持
if (stylus) {
const {
loader: stylusLoader = 'stylus-loader',
options: stylusOptions = { preferPathResolver: 'webpack' },
poststylus,
} = opts.stylus || {};

// 避免项目启动时出现异常 Error: Cannot find module 'stylus-loader'
let stylusLoaderPath;
try {
stylusLoaderPath = require.resolve(stylusLoader);
// eslint-disable-next-line no-empty
} catch (e) {}

// 如果安装了 stylus-loader 模块
if (stylusLoaderPath) {
rule
.use('stylus-loader')
.loader(stylusLoaderPath)
.options(stylusOptions);

// 如果使用了 poststylus, 需要移除 postcss-loader
if (poststylus) {
rule.delete('postcss-loader');
}
}
}
}

if (opts.cssModulesExcludes) {
Expand All @@ -146,6 +175,7 @@ export default function(webpackConfig, opts) {
applyCSSRules(config, {
less: ext === '.less',
sass: ext === '.sass' || ext === '.scss',
stylus: ext === '.styl' || ext === '.stylus',
});
});
}
Expand All @@ -162,14 +192,18 @@ export default function(webpackConfig, opts) {
cssModules: true,
sass: true,
});
applyCSSRules(webpackConfig.module.rule('.module.stylus').test(/\.module\.styl(us)?$/), {
cssModules: true,
stylus: true,
});
}

function cssExclude(filePath) {
if (/node_modules/.test(filePath)) {
return true;
}
if (opts.cssModulesWithAffix) {
if (/\.module\.(css|less|sass|scss)$/.test(filePath)) return true;
if (/\.module\.(css|less|sass|scss|styl(us)?)$/.test(filePath)) return true;
}
if (opts.cssModulesExcludes) {
for (const exclude of opts.cssModulesExcludes) {
Expand Down Expand Up @@ -239,6 +273,27 @@ export default function(webpackConfig, opts) {
sass: true,
},
);
applyCSSRules(
webpackConfig.module
.rule('stylus')
.test(/\.styl(us)?$/)
.exclude.add(cssExclude)
.end(),
{
cssModules: !opts.disableCSSModules,
stylus: true,
},
);
applyCSSRules(
webpackConfig.module
.rule('stylus-in-node_modules')
.test(/\.styl(us)?$/)
.include.add(/node_modules/)
.end(),
{
stylus: true,
},
);

const hash = !isDev && opts.hash ? '.[contenthash:8]' : '';
if (!opts.ssr && !opts.styleLoader) {
Expand Down
4 changes: 2 additions & 2 deletions packages/af-webpack/src/getWebpackConfig/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ import { EOL } from 'os';
import assert from 'assert';
import { getPkgPath, shouldTransform } from './es5ImcompatibleVersions';
import resolveDefine from './resolveDefine';
import send, { STARTING } from '../send';
// import send, { STARTING } from '../send';

function makeArray(item) {
if (Array.isArray(item)) return item;
Expand Down Expand Up @@ -96,7 +96,7 @@ export default function(opts) {
.exclude.add(/\.json$/)
.add(/\.(js|jsx|ts|tsx|mjs|wasm)$/)
.add(/\.(graphql|gql)$/)
.add(/\.(css|less|scss|sass)$/);
.add(/\.(css|less|scss|sass|styl(us)?)$/);
if (opts.urlLoaderExcludes) {
opts.urlLoaderExcludes.forEach(exclude => {
rule.add(exclude);
Expand Down
4 changes: 4 additions & 0 deletions packages/af-webpack/test/fixtures/stylus/expected/index.css
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#a .b {
background: #333;
}

112 changes: 112 additions & 0 deletions packages/af-webpack/test/fixtures/stylus/expected/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
/******/ (function(modules) { // webpackBootstrap
/******/ // The module cache
/******/ var installedModules = {};
/******/
/******/ // The require function
/******/ function __webpack_require__(moduleId) {
/******/
/******/ // Check if module is in cache
/******/ if(installedModules[moduleId]) {
/******/ return installedModules[moduleId].exports;
/******/ }
/******/ // Create a new module (and put it into the cache)
/******/ var module = installedModules[moduleId] = {
/******/ i: moduleId,
/******/ l: false,
/******/ exports: {}
/******/ };
/******/
/******/ // Execute the module function
/******/ modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
/******/
/******/ // Flag the module as loaded
/******/ module.l = true;
/******/
/******/ // Return the exports of the module
/******/ return module.exports;
/******/ }
/******/
/******/
/******/ // expose the modules object (__webpack_modules__)
/******/ __webpack_require__.m = modules;
/******/
/******/ // expose the module cache
/******/ __webpack_require__.c = installedModules;
/******/
/******/ // define getter function for harmony exports
/******/ __webpack_require__.d = function(exports, name, getter) {
/******/ if(!__webpack_require__.o(exports, name)) {
/******/ Object.defineProperty(exports, name, { enumerable: true, get: getter });
/******/ }
/******/ };
/******/
/******/ // define __esModule on exports
/******/ __webpack_require__.r = function(exports) {
/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) {
/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' });
/******/ }
/******/ Object.defineProperty(exports, '__esModule', { value: true });
/******/ };
/******/
/******/ // create a fake namespace object
/******/ // mode & 1: value is a module id, require it
/******/ // mode & 2: merge all properties of value into the ns
/******/ // mode & 4: return value when already ns object
/******/ // mode & 8|1: behave like require
/******/ __webpack_require__.t = function(value, mode) {
/******/ if(mode & 1) value = __webpack_require__(value);
/******/ if(mode & 8) return value;
/******/ if((mode & 4) && typeof value === 'object' && value && value.__esModule) return value;
/******/ var ns = Object.create(null);
/******/ __webpack_require__.r(ns);
/******/ Object.defineProperty(ns, 'default', { enumerable: true, value: value });
/******/ if(mode & 2 && typeof value != 'string') for(var key in value) __webpack_require__.d(ns, key, function(key) { return value[key]; }.bind(null, key));
/******/ return ns;
/******/ };
/******/
/******/ // getDefaultExport function for compatibility with non-harmony modules
/******/ __webpack_require__.n = function(module) {
/******/ var getter = module && module.__esModule ?
/******/ function getDefault() { return module['default']; } :
/******/ function getModuleExports() { return module; };
/******/ __webpack_require__.d(getter, 'a', getter);
/******/ return getter;
/******/ };
/******/
/******/ // Object.prototype.hasOwnProperty.call
/******/ __webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
/******/
/******/ // __webpack_public_path__
/******/ __webpack_require__.p = "";
/******/
/******/
/******/ // Load entry module and return exports
/******/ return __webpack_require__(__webpack_require__.s = 0);
/******/ })
/************************************************************************/
/******/ ([
/* 0 */
/***/ (function(module, exports, __webpack_require__) {

module.exports = __webpack_require__(1);


/***/ }),
/* 1 */
/***/ (function(module, exports, __webpack_require__) {

"use strict";


__webpack_require__(2);

alert(1);

/***/ }),
/* 2 */
/***/ (function(module, exports, __webpack_require__) {

// extracted by mini-css-extract-plugin

/***/ })
/******/ ]);
3 changes: 3 additions & 0 deletions packages/af-webpack/test/fixtures/stylus/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import './index.styl';

alert(1);
5 changes: 5 additions & 0 deletions packages/af-webpack/test/fixtures/stylus/index.styl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
$primary-color = #333

#a
.b
background $primary-color
2 changes: 1 addition & 1 deletion packages/umi-build-dev/src/getWebpackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -56,7 +56,7 @@ export default function(service: IApi, opts: IOpts = {}) {
);
const nodeExternalsOpts = {
whitelist: [
/\.(css|less|sass|scss)$/,
/\.(css|less|sass|scss|styl(us)?)$/,
/^umi(\/.*)?$/,
'umi-plugin-locale',
...(typeof ssr === 'object' && ssr.externalWhitelist ? ssr.externalWhitelist : []),
Expand Down
2 changes: 2 additions & 0 deletions packages/umi-build-dev/src/plugins/global-css.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import { existsSync } from 'fs';
export default function(api) {
const { paths, winPath } = api;
const cssFiles = [
join(paths.absSrcPath, 'global.styl'),
join(paths.absSrcPath, 'global.stylus'),
join(paths.absSrcPath, 'global.sass'),
join(paths.absSrcPath, 'global.scss'),
join(paths.absSrcPath, 'global.less'),
Expand Down

0 comments on commit b65a74e

Please sign in to comment.