From d5fffd7382e13a6ec768b636bdb6934f57b9f74b Mon Sep 17 00:00:00 2001 From: iChenLei Date: Tue, 31 May 2022 14:14:14 +0800 Subject: [PATCH] fix: yarn bin execute regression --- .github/workflows/ci.yml | 211 ++--- bin/stylus | 1692 +++++++++++++++++++------------------- package.json | 102 +-- test/yarn/package.json | 6 + 4 files changed, 1018 insertions(+), 993 deletions(-) create mode 100644 test/yarn/package.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index d7212ce9b..ddfcff825 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -1,96 +1,115 @@ -# Github actions workflow name -name: CI - -# Triggers the workflow on push or pull request events -on: - push: - branches: [main, dev, master] - tags: ['**'] - pull_request: - -jobs: - node_tests: - name: 'Test stylus on ${{matrix.os}} with node16' - strategy: - matrix: - os: [ubuntu-latest, macos-latest, windows-latest] - runs-on: ${{ matrix.os }} - steps: - # Pull repo to test machine - - uses: actions/checkout@v3 - # Configures the node version used on GitHub-hosted runners - - uses: actions/setup-node@v3 - with: - # The Node.js version to configure - node-version: '16' - # Caching dependencies to speed up workflows - - name: Get npm cache directory - id: npm-cache-dir - run: | - echo "::set-output name=dir::$(npm config get cache)" - - uses: actions/cache@v3 - id: npm-cache # use this to check for `cache-hit` ==> if: steps.npm-cache.outputs.cache-hit != 'true' - with: - path: ${{ steps.npm-cache-dir.outputs.dir }} - key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} - restore-keys: | - ${{ runner.os }}-node- - - name: Install npm dependencies - run: npm install - - name: Print put node & npm version - # Output useful info for debugging. - run: node --version && npm --version - - name: Run Test - run: npm run test - # Not work currently, need investigate - #- name: Run Test Cov - # run: npm run test-cov - - compat_node_tests: - name: 'Compat test stylus on ${{matrix.os}} with node${{matrix.node}}' - strategy: - matrix: - os: [ubuntu-latest] - node: [10, 12, 14, 18] - runs-on: ${{ matrix.os }} - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: ${{ matrix.node }} - - name: Install npm dependencies - run: npm install - - name: Print put node & npm version - run: node --version && npm --version - - name: Run Test - run: npm run test - - benchmark: - name: 'Run stylus benchmark with node18' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: '18' - - name: Install npm dependencies - run: npm install - - name: Print put node & npm version - run: node --version && npm --version - - name: Run Benchmark - run: node ./bm.js - - coverage: - name: 'Run nyc for code coverage' - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@v3 - - uses: actions/setup-node@v3 - with: - node-version: '18' - - name: Print put node & npm version - run: node --version && npm --version - - name: Install npm dependencies - run: npm install - - name: Run nyc - run: npx nyc@latest npm run test +# Github actions workflow name +name: CI + +# Triggers the workflow on push or pull request events +on: + push: + branches: [main, dev, master] + tags: ['**'] + pull_request: + +jobs: + node_tests: + name: 'Test stylus on ${{matrix.os}} with node16' + strategy: + matrix: + os: [ubuntu-latest, macos-latest, windows-latest] + runs-on: ${{ matrix.os }} + steps: + # Pull repo to test machine + - uses: actions/checkout@v3 + # Configures the node version used on GitHub-hosted runners + - uses: actions/setup-node@v3 + with: + # The Node.js version to configure + node-version: '16' + # Caching dependencies to speed up workflows + - name: Get npm cache directory + id: npm-cache-dir + run: | + echo "::set-output name=dir::$(npm config get cache)" + - uses: actions/cache@v3 + id: npm-cache # use this to check for `cache-hit` ==> if: steps.npm-cache.outputs.cache-hit != 'true' + with: + path: ${{ steps.npm-cache-dir.outputs.dir }} + key: ${{ runner.os }}-node-${{ hashFiles('**/package-lock.json') }} + restore-keys: | + ${{ runner.os }}-node- + - name: Install npm dependencies + run: npm install + - name: Print put node & npm version + # Output useful info for debugging. + run: node --version && npm --version + - name: Run Test + run: npm run test + # Not work currently, need investigate + #- name: Run Test Cov + # run: npm run test-cov + + compat_node_tests: + name: 'Compat test stylus on ${{matrix.os}} with node${{matrix.node}}' + strategy: + matrix: + os: [ubuntu-latest] + node: [10, 12, 14, 18] + runs-on: ${{ matrix.os }} + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: ${{ matrix.node }} + - name: Install npm dependencies + run: npm install + - name: Print put node & npm version + run: node --version && npm --version + - name: Run Test + run: npm run test + + benchmark: + name: 'Run stylus benchmark with node18' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Install npm dependencies + run: npm install + - name: Print put node & npm version + run: node --version && npm --version + - name: Run Benchmark + run: node ./bm.js + + yarn-regression: + name: 'yarn exec stylus regression test' + runs-on: macos-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Install npm dependencies + run: npm install + - name: Print put node & npm version + run: node --version && npm --version + - name: Install yarn global + run: npm install -g yarn + - name: Run Yarn + run: cd ./test/yarn + run: yarn install + run: yarn run stylus --version + + coverage: + name: 'Run nyc for code coverage' + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v3 + - uses: actions/setup-node@v3 + with: + node-version: '18' + - name: Print put node & npm version + run: node --version && npm --version + - name: Install npm dependencies + run: npm install + - name: Run nyc + run: npx nyc@latest npm run test diff --git a/bin/stylus b/bin/stylus index df9a18b63..d842645ac 100755 --- a/bin/stylus +++ b/bin/stylus @@ -1,846 +1,846 @@ -#!/usr/bin/env node - -/** - * Module dependencies. - */ - -var fs = require('fs') - , stylus = require('../lib/stylus') - , basename = require('path').basename - , dirname = require('path').dirname - , extname = require('path').extname - , resolve = require('path').resolve - , join = require('path').join - , isWindows = process.platform === 'win32' - , mkdirSync = fs.mkdirSync; - -/** - * Arguments. - */ - -var args = process.argv.slice(2); - -/** - * Compare flag. - */ - -var compare = false; - -/** - * Compress flag. - */ - -var compress = false; - -/** - * CSS conversion flag. - */ - -var convertCSS = false; - -/** - * Line numbers flag. - */ - -var linenos = false; - -/** - * CSS class prefix. - */ -var prefix = ''; - -/** - * Print to stdout flag. - */ -var print = false; - -/** - * Firebug flag - */ - -var firebug = false; - -/** - * Quiet flag - */ - -var quiet = false; - -/** - * Sourcemap flag - */ - -var sourcemap = false; - -/** - * Files to processes. - */ - -var files = []; - -/** - * Import paths. - */ - -var paths = []; - -/** - * Destination directory. - */ - -var dest; - -/** - * Watcher hash. - */ - -var watchers; - -/** - * Enable REPL. - */ - -var interactive; - -/** - * Plugins. - */ - -var plugins = []; - -/** - * Optional url() function. - */ - -var urlFunction = false; - -/** - * Include CSS on import. - */ - -var includeCSS = false; - -/** - * Set file imports. - */ - -var imports = []; - -/** - * Resolve relative urls - */ - -var resolveURL = false; - -/** - * Disable cache. - */ - -var disableCache = false; - -/** - * Display dependencies flag. - */ - -var deps = false; - -/** - * Hoist at-rules. - */ - -var hoist = false; - -/** - * Specify custom file extension. - */ - -var ext = '.css'; - -/** - * Usage docs. - */ - -var usage = [ - '' - , ' Usage: stylus [options] [command] [< in [> out]]' - , ' [file|dir ...]' - , '' - , ' Commands:' - , '' - , ' help [:] Opens help info at MDN for in' - , ' your default browser. Optionally' - , ' searches other resources of :' - , ' safari opera w3c ms caniuse quirksmode' - , '' - , ' Options:' - , '' - , ' -i, --interactive Start interactive REPL' - , ' -u, --use Utilize the Stylus plugin at ' - , ' -U, --inline Utilize image inlining via data URI support' - , ' -w, --watch Watch file(s) for changes and re-compile' - , ' -o, --out Output to when passing files' - , ' -C, --css [dest] Convert CSS input to Stylus' - , ' -I, --include Add to lookup paths' - , ' -c, --compress Compress CSS output' - , ' -d, --compare Display input along with output' - , ' -f, --firebug Emits debug infos in the generated CSS that' - , ' can be used by the FireStylus Firebug plugin' - , ' -l, --line-numbers Emits comments in the generated CSS' - , ' indicating the corresponding Stylus line' - , ' -m, --sourcemap Generates a sourcemap in sourcemaps v3 format' - , ' -q, --quiet Less noisy output' - , ' --sourcemap-inline Inlines sourcemap with full source text in base64 format' - , ' --sourcemap-root "sourceRoot" property of the generated sourcemap' - , ' --sourcemap-base Base from which sourcemap and all sources are relative' - , ' -P, --prefix [prefix] prefix all css classes' - , ' -p, --print Print out the compiled CSS' - , ' --import Import stylus ' - , ' --include-css Include regular CSS on @import' - , ' --ext Specify custom file extension for compiled file, default .css' - , ' -D, --deps Display dependencies of the compiled file' - , ' --disable-cache Disable caching' - , ' --hoist-atrules Move @import and @charset to the top' - , ' -r, --resolve-url Resolve relative urls inside imports' - , ' --resolve-url-nocheck Like --resolve-url but without file existence check' - , ' -V, --version Display the version of Stylus' - , ' -h, --help Display help information' - , '' -].join('\n'); - -/** - * Handle arguments. - */ - -var arg; -while (args.length) { - arg = args.shift(); - switch (arg) { - case '-h': - case '--help': - console.error(usage); - return; - case '-d': - case '--compare': - compare = true; - break; - case '-c': - case '--compress': - compress = true; - break; - case '-C': - case '--css': - convertCSS = true; - break; - case '-f': - case '--firebug': - firebug = true; - break; - case '-l': - case '--line-numbers': - linenos = true; - break; - case '-m': - case '--sourcemap': - sourcemap = {}; - break; - case '-q': - case '--quiet': - quiet = true; - break; - case '--sourcemap-inline': - sourcemap = sourcemap || {}; - sourcemap.inline = true; - break; - case '--sourcemap-root': - var url = args.shift(); - if (!url) throw new Error('--sourcemap-root required'); - sourcemap = sourcemap || {}; - sourcemap.sourceRoot = url; - break; - case '--sourcemap-base': - var path = args.shift(); - if (!path) throw new Error('--sourcemap-base required'); - sourcemap = sourcemap || {}; - sourcemap.basePath = path; - break; - case '-P': - case '--prefix': - prefix = args.shift(); - if (!prefix) throw new Error('--prefix required'); - break; - case '-p': - case '--print': - print = true; - break; - case '-V': - case '--version': - console.log(stylus.version); - return; - case '-o': - case '--out': - dest = args.shift(); - if (!dest) throw new Error('--out required'); - break; - case 'help': - var name = args.shift() - , browser = name.split(':'); - if (browser.length > 1) { - name = [].slice.call(browser, 1).join(':'); - browser = browser[0]; - } else { - name = browser[0]; - browser = ''; - } - if (!name) throw new Error('help required'); - help(name); - break; - case '--include-css': - includeCSS = true; - break; - case '--ext': - ext = args.shift(); - if (!ext) throw new Error('--ext required'); - break; - case '--disable-cache': - disableCache = true; - break; - case '--hoist-atrules': - hoist = true; - break; - case '-i': - case '--repl': - case '--interactive': - interactive = true; - break; - case '-I': - case '--include': - var path = args.shift(); - if (!path) throw new Error('--include required'); - paths.push(path); - break; - case '-w': - case '--watch': - watchers = {}; - break; - case '-U': - case '--inline': - args.unshift('--use', 'url'); - break; - case '-u': - case '--use': - var options; - var path = args.shift(); - if (!path) throw new Error('--use required'); - - // options - if ('--with' == args[0]) { - args.shift(); - options = args.shift(); - if (!options) throw new Error('--with required'); - options = eval('(' + options + ')'); - } - - // url support - if ('url' == path) { - urlFunction = options || {}; - } else { - paths.push(dirname(path)); - plugins.push({ path: path, options: options }); - } - break; - case '--import': - var file = args.shift(); - if (!file) throw new Error('--import required'); - imports.push(file); - break; - case '-r': - case '--resolve-url': - resolveURL = {}; - break; - case '--resolve-url-nocheck': - resolveURL = { nocheck: true }; - break; - case '-D': - case '--deps': - deps = true; - break; - default: - files.push(arg); - } -} - -// if --watch is used, assume we are -// not working with stdio - -if (watchers && !files.length) { - files = fs.readdirSync(process.cwd()) - .filter(function(file){ - return file.match(/\.styl$/); - }); -} - -// --sourcemap flag is not working with stdio -if (sourcemap && !files.length) sourcemap = false; - -/** - * Open the default browser to the CSS property `name`. - * - * @param {String} name - */ - -function help(name) { - var url - , exec = require('child_process').exec - , command; - - name = encodeURIComponent(name); - - switch (browser) { - case 'safari': - case 'webkit': - url = 'https://developer.apple.com/library/safari/search/?q=' + name; - break; - case 'opera': - url = 'http://dev.opera.com/search/?term=' + name; - break; - case 'w3c': - url = 'http://www.google.com/search?q=site%3Awww.w3.org%2FTR+' + name; - break; - case 'ms': - url = 'http://social.msdn.microsoft.com/search/en-US/ie?query=' + name + '&refinement=59%2c61'; - break; - case 'caniuse': - url = 'http://caniuse.com/#search=' + name; - break; - case 'quirksmode': - url = 'http://www.google.com/search?q=site%3Awww.quirksmode.org+' + name; - break; - default: - url = 'https://developer.mozilla.org/en/CSS/' + name; - } - - switch (process.platform) { - case 'linux': command = 'x-www-browser'; break; - default: command = 'open'; - } - - exec(command + ' "' + url + '"', function(){ - process.exit(0); - }); -} - -// Compilation options - -if (files.length > 1 && isCSS(dest)) { - dest = dirname(dest); -} - -var options = { - filename: 'stdin' - , compress: compress - , firebug: firebug - , linenos: linenos - , sourcemap: sourcemap - , paths: [process.cwd()].concat(paths) - , prefix: prefix - , dest: dest - , 'hoist atrules': hoist -}; - -// Buffer stdin - -var str = ''; - -// Convert CSS to Stylus - -if (convertCSS) { - switch (files.length) { - case 2: - compileCSSFile(files[0], files[1]); - break; - case 1: - var file = files[0]; - compileCSSFile(file, join(dirname(file), basename(file, extname(file))) + '.styl'); - break; - default: - var stdin = process.openStdin(); - stdin.setEncoding('utf8'); - stdin.on('data', function(chunk){ str += chunk; }); - stdin.on('end', function(){ - var out = stylus.convertCSS(str); - console.log(out); - }); - } -} else if (interactive) { - repl(); -} else if (deps) { - // if --deps is used, just display list of the dependencies - // not working with stdio and dirs - displayDeps(); -} else { - if (files.length) { - compileFiles(files); - } else { - compileStdio(); - } -} - -/** - * Start Stylus REPL. - */ - -function repl() { - var options = { cache: false, filename: 'stdin', imports: [join(__dirname, '..', 'lib', 'functions')] } - , parser = new stylus.Parser('', options) - , evaluator = new stylus.Evaluator(parser.parse(), options) - , rl = require('readline') - , repl = rl.createInterface(process.stdin, process.stdout, autocomplete) - , global = evaluator.global.scope; - - // expose BIFs - evaluator.evaluate(); - - // readline - repl.setPrompt('> '); - repl.prompt(); - - // HACK: flat-list auto-complete - function autocomplete(line){ - var out = process.stdout - , keys = Object.keys(global.locals) - , len = keys.length - , words = line.split(/\s+/) - , word = words.pop() - , names = [] - , name - , node - , key; - - // find words that match - for (var i = 0; i < len; ++i) { - key = keys[i]; - if (0 == key.indexOf(word)) { - node = global.lookup(key); - switch (node.nodeName) { - case 'function': - names.push(node.toString()); - break; - default: - names.push(key); - } - } - } - - return [names, line]; - }; - - repl.on('line', function(line){ - if (!line.trim().length) return repl.prompt(); - parser = new stylus.Parser(line, options); - parser.state.push('expression'); - evaluator.return = true; - try { - var expr = parser.parse(); - evaluator.root = expr; - var ret = evaluator.evaluate(); - var node; - while (node = ret.nodes.pop()) { - if (!node.suppress) { - var str = node.toString(); - if ('(' == str[0]) str = str.replace(/^\(|\)$/g, ''); - console.log('\033[90m=> \033[0m' + highlight(str)); - break; - } - } - repl.prompt(); - } catch (err) { - console.error('\033[31merror: %s\033[0m', err.message || err.stack); - repl.prompt(); - } - }); - - repl.on('SIGINT', function(){ - console.log(); - process.exit(0); - }); -} - -/** - * Highlight the given string of Stylus. - */ - -function highlight(str) { - return str - .replace(/(#)?(\d+(\.\d+)?)/g, function($0, $1, $2){ - return $1 ? $0 : '\033[36m' + $2 + '\033[0m'; - }) - .replace(/(#[\da-fA-F]+)/g, '\033[33m$1\033[0m') - .replace(/('.*?'|".*?")/g, '\033[32m$1\033[0m'); -} - -/** - * Convert a CSS file to a Styl file - */ - -function compileCSSFile(file, fileOut) { - fs.stat(file, function(err, stat){ - if (err) throw err; - if (stat.isFile()) { - fs.readFile(file, 'utf8', function(err, str){ - if (err) throw err; - var styl = stylus.convertCSS(str); - fs.writeFile(fileOut, styl, function(err){ - if (err) throw err; - }); - }); - } - }); -} - -/** - * Compile with stdio. - */ - -function compileStdio() { - process.stdin.setEncoding('utf8'); - process.stdin.on('data', function(chunk){ str += chunk; }); - process.stdin.on('end', function(){ - // Compile to css - var style = stylus(str, options); - if (includeCSS) style.set('include css', true); - if (disableCache) style.set('cache', false); - usePlugins(style); - importFiles(style); - style.render(function(err, css){ - if (err) throw err; - if (compare) { - console.log('\n\x1b[1mInput:\x1b[0m'); - console.log(str); - console.log('\n\x1b[1mOutput:\x1b[0m'); - } - console.log(css); - if (compare) console.log(); - }); - }).resume(); -} - -/** - * Compile the given files. - */ - -function compileFiles(files) { - files.forEach(compileFile); -} - -/** - * Display dependencies of the compiled files. - */ - -function displayDeps() { - files.forEach(function(file){ - // ensure file exists - fs.stat(file, function(err, stat){ - if (err) throw err; - fs.readFile(file, 'utf8', function(err, str){ - if (err) throw err; - options.filename = file; - var style = stylus(str, options); - - usePlugins(style); - importFiles(style); - console.log(style.deps().join('\n')); - }); - }); - }); -} - -/** - * Compile the given file. - */ - -function compileFile(file) { - // ensure file exists - fs.stat(file, function(err, stat){ - if (err) throw err; - // file - if (stat.isFile()) { - fs.readFile(file, 'utf8', function(err, str){ - if (err) throw err; - options.filename = file; - options._imports = []; - var style = stylus(str, options); - if (includeCSS) style.set('include css', true); - if (disableCache) style.set('cache', false); - if (sourcemap) style.set('sourcemap', sourcemap); - - usePlugins(style); - importFiles(style); - style.render(function(err, css){ - watchImports(file, options._imports); - if (err) { - if (watchers) { - console.error(err.stack || err.message); - } else { - throw err; - } - } else { - writeFile(file, css); - // write sourcemap - if (sourcemap && !sourcemap.inline) { - writeSourcemap(file, style.sourcemap); - } - } - }); - }); - // directory - } else if (stat.isDirectory()) { - fs.readdir(file, function(err, files){ - if (err) throw err; - files.filter(function(path){ - return path.match(/\.styl$/); - }).map(function(path){ - return join(file, path); - }).forEach(compileFile); - }); - } - }); -} - -/** - * Write the given CSS output. - */ - -function createPath(file, sourceMap) { - var out; - if (files.length === 1 && isCSS(dest)) { - return [dest, sourceMap ? '.map' : ''].join(''); - } - // --out support - out = [basename(file, extname(file)), sourceMap ? ext + '.map' : ext].join(''); - return dest - ? join(dest, out) - : join(dirname(file), out); -} - -/** - * Check if the given path is a CSS file. - */ - -function isCSS(file) { - return file && '.css' === extname(file); -} - -function writeFile(file, css) { - // --print support - if (print) return process.stdout.write(css); - var path = createPath(file); - - mkdirSync(dirname(path), { recursive: true }) - - fs.writeFile(path, css, function(err){ - if (err) throw err; - console.log(' \033[90mcompiled\033[0m %s', path); - // --watch support - watch(file, file); - }); -} - -/** -* Write the given sourcemap. -*/ - -function writeSourcemap(file, sourcemap) { - var path = createPath(file, true); - - mkdirSync(dirname(path), { recursive: true }) - - fs.writeFile(path, JSON.stringify(sourcemap), function(err){ - if (err) throw err; - // don't output log message if --print is present - if (!print) console.log(' \033[90mgenerated\033[0m %s', path); - }); -} - -/** - * Watch the given `file` and recompiling `rootFile` when modified. - */ - -function watch(file, rootFile) { - // not watching - if (!watchers) return; - - // already watched - if (watchers[file]) { - watchers[file][rootFile] = true; - return; - } - - // watch the file itself - watchers[file] = {}; - watchers[file][rootFile] = true; - if (print) { - console.error('Stylus CLI Error: Watch and print cannot be used together'); - process.exit(1); - } - if(!quiet){ - console.log(' \033[90mwatching\033[0m %s', file); - } - // if is windows use fs.watch api instead - // TODO: remove watchFile when fs.watch() works on osx etc - if (isWindows) { - fs.watch(file, compile); - } else { - fs.watchFile(file, { interval: 300 }, function(curr, prev) { - if (curr.mtime > prev.mtime) compile(); - }); - } - - function compile() { - for (var rootFile in watchers[file]) { - compileFile(rootFile); - } - } -} - -/** - * Watch `imports`, re-compiling `file` when they change. - */ - -function watchImports(file, imports) { - imports.forEach(function(imported){ - if (!imported.path) return; - watch(imported.path, file); - }); -} - -/** - * Utilize plugins. - */ - -function usePlugins(style) { - plugins.forEach(function(plugin){ - var path = plugin.path; - var options = plugin.options; - fn = require(/^\.+\//.test(path) ? resolve(path) : path); - if ('function' != typeof fn) { - throw new Error('plugin ' + path + ' does not export a function'); - } - style.use(fn(options)); - }); - - if (urlFunction) { - style.define('url', stylus.url(urlFunction)); - } else if (resolveURL) { - style.define('url', stylus.resolver(resolveURL)); - } -} - -/** - * Imports the indicated files. - */ - -function importFiles(style) { - imports.forEach(function(file) { - style.import(file); - }); -} +#!/usr/bin/env node + +/** + * Module dependencies. + */ + +var fs = require('fs') + , stylus = require('../lib/stylus') + , basename = require('path').basename + , dirname = require('path').dirname + , extname = require('path').extname + , resolve = require('path').resolve + , join = require('path').join + , isWindows = process.platform === 'win32' + , mkdirSync = fs.mkdirSync; + +/** + * Arguments. + */ + +var args = process.argv.slice(2); + +/** + * Compare flag. + */ + +var compare = false; + +/** + * Compress flag. + */ + +var compress = false; + +/** + * CSS conversion flag. + */ + +var convertCSS = false; + +/** + * Line numbers flag. + */ + +var linenos = false; + +/** + * CSS class prefix. + */ +var prefix = ''; + +/** + * Print to stdout flag. + */ +var print = false; + +/** + * Firebug flag + */ + +var firebug = false; + +/** + * Quiet flag + */ + +var quiet = false; + +/** + * Sourcemap flag + */ + +var sourcemap = false; + +/** + * Files to processes. + */ + +var files = []; + +/** + * Import paths. + */ + +var paths = []; + +/** + * Destination directory. + */ + +var dest; + +/** + * Watcher hash. + */ + +var watchers; + +/** + * Enable REPL. + */ + +var interactive; + +/** + * Plugins. + */ + +var plugins = []; + +/** + * Optional url() function. + */ + +var urlFunction = false; + +/** + * Include CSS on import. + */ + +var includeCSS = false; + +/** + * Set file imports. + */ + +var imports = []; + +/** + * Resolve relative urls + */ + +var resolveURL = false; + +/** + * Disable cache. + */ + +var disableCache = false; + +/** + * Display dependencies flag. + */ + +var deps = false; + +/** + * Hoist at-rules. + */ + +var hoist = false; + +/** + * Specify custom file extension. + */ + +var ext = '.css'; + +/** + * Usage docs. + */ + +var usage = [ + '' + , ' Usage: stylus [options] [command] [< in [> out]]' + , ' [file|dir ...]' + , '' + , ' Commands:' + , '' + , ' help [:] Opens help info at MDN for in' + , ' your default browser. Optionally' + , ' searches other resources of :' + , ' safari opera w3c ms caniuse quirksmode' + , '' + , ' Options:' + , '' + , ' -i, --interactive Start interactive REPL' + , ' -u, --use Utilize the Stylus plugin at ' + , ' -U, --inline Utilize image inlining via data URI support' + , ' -w, --watch Watch file(s) for changes and re-compile' + , ' -o, --out Output to when passing files' + , ' -C, --css [dest] Convert CSS input to Stylus' + , ' -I, --include Add to lookup paths' + , ' -c, --compress Compress CSS output' + , ' -d, --compare Display input along with output' + , ' -f, --firebug Emits debug infos in the generated CSS that' + , ' can be used by the FireStylus Firebug plugin' + , ' -l, --line-numbers Emits comments in the generated CSS' + , ' indicating the corresponding Stylus line' + , ' -m, --sourcemap Generates a sourcemap in sourcemaps v3 format' + , ' -q, --quiet Less noisy output' + , ' --sourcemap-inline Inlines sourcemap with full source text in base64 format' + , ' --sourcemap-root "sourceRoot" property of the generated sourcemap' + , ' --sourcemap-base Base from which sourcemap and all sources are relative' + , ' -P, --prefix [prefix] prefix all css classes' + , ' -p, --print Print out the compiled CSS' + , ' --import Import stylus ' + , ' --include-css Include regular CSS on @import' + , ' --ext Specify custom file extension for compiled file, default .css' + , ' -D, --deps Display dependencies of the compiled file' + , ' --disable-cache Disable caching' + , ' --hoist-atrules Move @import and @charset to the top' + , ' -r, --resolve-url Resolve relative urls inside imports' + , ' --resolve-url-nocheck Like --resolve-url but without file existence check' + , ' -V, --version Display the version of Stylus' + , ' -h, --help Display help information' + , '' +].join('\n'); + +/** + * Handle arguments. + */ + +var arg; +while (args.length) { + arg = args.shift(); + switch (arg) { + case '-h': + case '--help': + console.error(usage); + return; + case '-d': + case '--compare': + compare = true; + break; + case '-c': + case '--compress': + compress = true; + break; + case '-C': + case '--css': + convertCSS = true; + break; + case '-f': + case '--firebug': + firebug = true; + break; + case '-l': + case '--line-numbers': + linenos = true; + break; + case '-m': + case '--sourcemap': + sourcemap = {}; + break; + case '-q': + case '--quiet': + quiet = true; + break; + case '--sourcemap-inline': + sourcemap = sourcemap || {}; + sourcemap.inline = true; + break; + case '--sourcemap-root': + var url = args.shift(); + if (!url) throw new Error('--sourcemap-root required'); + sourcemap = sourcemap || {}; + sourcemap.sourceRoot = url; + break; + case '--sourcemap-base': + var path = args.shift(); + if (!path) throw new Error('--sourcemap-base required'); + sourcemap = sourcemap || {}; + sourcemap.basePath = path; + break; + case '-P': + case '--prefix': + prefix = args.shift(); + if (!prefix) throw new Error('--prefix required'); + break; + case '-p': + case '--print': + print = true; + break; + case '-V': + case '--version': + console.log(stylus.version); + return; + case '-o': + case '--out': + dest = args.shift(); + if (!dest) throw new Error('--out required'); + break; + case 'help': + var name = args.shift() + , browser = name.split(':'); + if (browser.length > 1) { + name = [].slice.call(browser, 1).join(':'); + browser = browser[0]; + } else { + name = browser[0]; + browser = ''; + } + if (!name) throw new Error('help required'); + help(name); + break; + case '--include-css': + includeCSS = true; + break; + case '--ext': + ext = args.shift(); + if (!ext) throw new Error('--ext required'); + break; + case '--disable-cache': + disableCache = true; + break; + case '--hoist-atrules': + hoist = true; + break; + case '-i': + case '--repl': + case '--interactive': + interactive = true; + break; + case '-I': + case '--include': + var path = args.shift(); + if (!path) throw new Error('--include required'); + paths.push(path); + break; + case '-w': + case '--watch': + watchers = {}; + break; + case '-U': + case '--inline': + args.unshift('--use', 'url'); + break; + case '-u': + case '--use': + var options; + var path = args.shift(); + if (!path) throw new Error('--use required'); + + // options + if ('--with' == args[0]) { + args.shift(); + options = args.shift(); + if (!options) throw new Error('--with required'); + options = eval('(' + options + ')'); + } + + // url support + if ('url' == path) { + urlFunction = options || {}; + } else { + paths.push(dirname(path)); + plugins.push({ path: path, options: options }); + } + break; + case '--import': + var file = args.shift(); + if (!file) throw new Error('--import required'); + imports.push(file); + break; + case '-r': + case '--resolve-url': + resolveURL = {}; + break; + case '--resolve-url-nocheck': + resolveURL = { nocheck: true }; + break; + case '-D': + case '--deps': + deps = true; + break; + default: + files.push(arg); + } +} + +// if --watch is used, assume we are +// not working with stdio + +if (watchers && !files.length) { + files = fs.readdirSync(process.cwd()) + .filter(function(file){ + return file.match(/\.styl$/); + }); +} + +// --sourcemap flag is not working with stdio +if (sourcemap && !files.length) sourcemap = false; + +/** + * Open the default browser to the CSS property `name`. + * + * @param {String} name + */ + +function help(name) { + var url + , exec = require('child_process').exec + , command; + + name = encodeURIComponent(name); + + switch (browser) { + case 'safari': + case 'webkit': + url = 'https://developer.apple.com/library/safari/search/?q=' + name; + break; + case 'opera': + url = 'http://dev.opera.com/search/?term=' + name; + break; + case 'w3c': + url = 'http://www.google.com/search?q=site%3Awww.w3.org%2FTR+' + name; + break; + case 'ms': + url = 'http://social.msdn.microsoft.com/search/en-US/ie?query=' + name + '&refinement=59%2c61'; + break; + case 'caniuse': + url = 'http://caniuse.com/#search=' + name; + break; + case 'quirksmode': + url = 'http://www.google.com/search?q=site%3Awww.quirksmode.org+' + name; + break; + default: + url = 'https://developer.mozilla.org/en/CSS/' + name; + } + + switch (process.platform) { + case 'linux': command = 'x-www-browser'; break; + default: command = 'open'; + } + + exec(command + ' "' + url + '"', function(){ + process.exit(0); + }); +} + +// Compilation options + +if (files.length > 1 && isCSS(dest)) { + dest = dirname(dest); +} + +var options = { + filename: 'stdin' + , compress: compress + , firebug: firebug + , linenos: linenos + , sourcemap: sourcemap + , paths: [process.cwd()].concat(paths) + , prefix: prefix + , dest: dest + , 'hoist atrules': hoist +}; + +// Buffer stdin + +var str = ''; + +// Convert CSS to Stylus + +if (convertCSS) { + switch (files.length) { + case 2: + compileCSSFile(files[0], files[1]); + break; + case 1: + var file = files[0]; + compileCSSFile(file, join(dirname(file), basename(file, extname(file))) + '.styl'); + break; + default: + var stdin = process.openStdin(); + stdin.setEncoding('utf8'); + stdin.on('data', function(chunk){ str += chunk; }); + stdin.on('end', function(){ + var out = stylus.convertCSS(str); + console.log(out); + }); + } +} else if (interactive) { + repl(); +} else if (deps) { + // if --deps is used, just display list of the dependencies + // not working with stdio and dirs + displayDeps(); +} else { + if (files.length) { + compileFiles(files); + } else { + compileStdio(); + } +} + +/** + * Start Stylus REPL. + */ + +function repl() { + var options = { cache: false, filename: 'stdin', imports: [join(__dirname, '..', 'lib', 'functions')] } + , parser = new stylus.Parser('', options) + , evaluator = new stylus.Evaluator(parser.parse(), options) + , rl = require('readline') + , repl = rl.createInterface(process.stdin, process.stdout, autocomplete) + , global = evaluator.global.scope; + + // expose BIFs + evaluator.evaluate(); + + // readline + repl.setPrompt('> '); + repl.prompt(); + + // HACK: flat-list auto-complete + function autocomplete(line){ + var out = process.stdout + , keys = Object.keys(global.locals) + , len = keys.length + , words = line.split(/\s+/) + , word = words.pop() + , names = [] + , name + , node + , key; + + // find words that match + for (var i = 0; i < len; ++i) { + key = keys[i]; + if (0 == key.indexOf(word)) { + node = global.lookup(key); + switch (node.nodeName) { + case 'function': + names.push(node.toString()); + break; + default: + names.push(key); + } + } + } + + return [names, line]; + }; + + repl.on('line', function(line){ + if (!line.trim().length) return repl.prompt(); + parser = new stylus.Parser(line, options); + parser.state.push('expression'); + evaluator.return = true; + try { + var expr = parser.parse(); + evaluator.root = expr; + var ret = evaluator.evaluate(); + var node; + while (node = ret.nodes.pop()) { + if (!node.suppress) { + var str = node.toString(); + if ('(' == str[0]) str = str.replace(/^\(|\)$/g, ''); + console.log('\033[90m=> \033[0m' + highlight(str)); + break; + } + } + repl.prompt(); + } catch (err) { + console.error('\033[31merror: %s\033[0m', err.message || err.stack); + repl.prompt(); + } + }); + + repl.on('SIGINT', function(){ + console.log(); + process.exit(0); + }); +} + +/** + * Highlight the given string of Stylus. + */ + +function highlight(str) { + return str + .replace(/(#)?(\d+(\.\d+)?)/g, function($0, $1, $2){ + return $1 ? $0 : '\033[36m' + $2 + '\033[0m'; + }) + .replace(/(#[\da-fA-F]+)/g, '\033[33m$1\033[0m') + .replace(/('.*?'|".*?")/g, '\033[32m$1\033[0m'); +} + +/** + * Convert a CSS file to a Styl file + */ + +function compileCSSFile(file, fileOut) { + fs.stat(file, function(err, stat){ + if (err) throw err; + if (stat.isFile()) { + fs.readFile(file, 'utf8', function(err, str){ + if (err) throw err; + var styl = stylus.convertCSS(str); + fs.writeFile(fileOut, styl, function(err){ + if (err) throw err; + }); + }); + } + }); +} + +/** + * Compile with stdio. + */ + +function compileStdio() { + process.stdin.setEncoding('utf8'); + process.stdin.on('data', function(chunk){ str += chunk; }); + process.stdin.on('end', function(){ + // Compile to css + var style = stylus(str, options); + if (includeCSS) style.set('include css', true); + if (disableCache) style.set('cache', false); + usePlugins(style); + importFiles(style); + style.render(function(err, css){ + if (err) throw err; + if (compare) { + console.log('\n\x1b[1mInput:\x1b[0m'); + console.log(str); + console.log('\n\x1b[1mOutput:\x1b[0m'); + } + console.log(css); + if (compare) console.log(); + }); + }).resume(); +} + +/** + * Compile the given files. + */ + +function compileFiles(files) { + files.forEach(compileFile); +} + +/** + * Display dependencies of the compiled files. + */ + +function displayDeps() { + files.forEach(function(file){ + // ensure file exists + fs.stat(file, function(err, stat){ + if (err) throw err; + fs.readFile(file, 'utf8', function(err, str){ + if (err) throw err; + options.filename = file; + var style = stylus(str, options); + + usePlugins(style); + importFiles(style); + console.log(style.deps().join('\n')); + }); + }); + }); +} + +/** + * Compile the given file. + */ + +function compileFile(file) { + // ensure file exists + fs.stat(file, function(err, stat){ + if (err) throw err; + // file + if (stat.isFile()) { + fs.readFile(file, 'utf8', function(err, str){ + if (err) throw err; + options.filename = file; + options._imports = []; + var style = stylus(str, options); + if (includeCSS) style.set('include css', true); + if (disableCache) style.set('cache', false); + if (sourcemap) style.set('sourcemap', sourcemap); + + usePlugins(style); + importFiles(style); + style.render(function(err, css){ + watchImports(file, options._imports); + if (err) { + if (watchers) { + console.error(err.stack || err.message); + } else { + throw err; + } + } else { + writeFile(file, css); + // write sourcemap + if (sourcemap && !sourcemap.inline) { + writeSourcemap(file, style.sourcemap); + } + } + }); + }); + // directory + } else if (stat.isDirectory()) { + fs.readdir(file, function(err, files){ + if (err) throw err; + files.filter(function(path){ + return path.match(/\.styl$/); + }).map(function(path){ + return join(file, path); + }).forEach(compileFile); + }); + } + }); +} + +/** + * Write the given CSS output. + */ + +function createPath(file, sourceMap) { + var out; + if (files.length === 1 && isCSS(dest)) { + return [dest, sourceMap ? '.map' : ''].join(''); + } + // --out support + out = [basename(file, extname(file)), sourceMap ? ext + '.map' : ext].join(''); + return dest + ? join(dest, out) + : join(dirname(file), out); +} + +/** + * Check if the given path is a CSS file. + */ + +function isCSS(file) { + return file && '.css' === extname(file); +} + +function writeFile(file, css) { + // --print support + if (print) return process.stdout.write(css); + var path = createPath(file); + + mkdirSync(dirname(path), { recursive: true }) + + fs.writeFile(path, css, function(err){ + if (err) throw err; + console.log(' \033[90mcompiled\033[0m %s', path); + // --watch support + watch(file, file); + }); +} + +/** +* Write the given sourcemap. +*/ + +function writeSourcemap(file, sourcemap) { + var path = createPath(file, true); + + mkdirSync(dirname(path), { recursive: true }) + + fs.writeFile(path, JSON.stringify(sourcemap), function(err){ + if (err) throw err; + // don't output log message if --print is present + if (!print) console.log(' \033[90mgenerated\033[0m %s', path); + }); +} + +/** + * Watch the given `file` and recompiling `rootFile` when modified. + */ + +function watch(file, rootFile) { + // not watching + if (!watchers) return; + + // already watched + if (watchers[file]) { + watchers[file][rootFile] = true; + return; + } + + // watch the file itself + watchers[file] = {}; + watchers[file][rootFile] = true; + if (print) { + console.error('Stylus CLI Error: Watch and print cannot be used together'); + process.exit(1); + } + if(!quiet){ + console.log(' \033[90mwatching\033[0m %s', file); + } + // if is windows use fs.watch api instead + // TODO: remove watchFile when fs.watch() works on osx etc + if (isWindows) { + fs.watch(file, compile); + } else { + fs.watchFile(file, { interval: 300 }, function(curr, prev) { + if (curr.mtime > prev.mtime) compile(); + }); + } + + function compile() { + for (var rootFile in watchers[file]) { + compileFile(rootFile); + } + } +} + +/** + * Watch `imports`, re-compiling `file` when they change. + */ + +function watchImports(file, imports) { + imports.forEach(function(imported){ + if (!imported.path) return; + watch(imported.path, file); + }); +} + +/** + * Utilize plugins. + */ + +function usePlugins(style) { + plugins.forEach(function(plugin){ + var path = plugin.path; + var options = plugin.options; + fn = require(/^\.+\//.test(path) ? resolve(path) : path); + if ('function' != typeof fn) { + throw new Error('plugin ' + path + ' does not export a function'); + } + style.use(fn(options)); + }); + + if (urlFunction) { + style.define('url', stylus.url(urlFunction)); + } else if (resolveURL) { + style.define('url', stylus.resolver(resolveURL)); + } +} + +/** + * Imports the indicated files. + */ + +function importFiles(style) { + imports.forEach(function(file) { + style.import(file); + }); +} diff --git a/package.json b/package.json index 6d16701f5..3209f6753 100644 --- a/package.json +++ b/package.json @@ -1,51 +1,51 @@ -{ - "name": "stylus", - "description": "Robust, expressive, and feature-rich CSS superset", - "version": "0.58.0", - "author": "TJ Holowaychuk ", - "keywords": [ - "css", - "parser", - "style", - "stylesheets", - "jade", - "language" - ], - "repository": { - "type": "git", - "url": "git://github.com/stylus/stylus" - }, - "main": "./index.js", - "browserify": "./lib/browserify.js", - "engines": { - "node": "*" - }, - "bin": { - "stylus": "./bin/stylus" - }, - "scripts": { - "prepublish": "npm prune", - "test": "mocha test/ test/middleware/ --require chai --bail --check-leaks --reporter dot" - }, - "dependencies": { - "css": "^3.0.0", - "debug": "^4.3.2", - "glob": "^7.1.6", - "sax": "~1.2.4", - "source-map": "^0.7.3" - }, - "devDependencies": { - "chai": "^4.3.6", - "mocha": "^9.2.0" - }, - "bugs": { - "url": "https://github.com/stylus/stylus/issues" - }, - "homepage": "https://github.com/stylus/stylus", - "directories": { - "doc": "docs", - "example": "examples", - "test": "test" - }, - "license": "MIT" -} +{ + "name": "stylus", + "description": "Robust, expressive, and feature-rich CSS superset", + "version": "0.58.0", + "author": "TJ Holowaychuk ", + "keywords": [ + "css", + "parser", + "style", + "stylesheets", + "jade", + "language" + ], + "repository": { + "type": "git", + "url": "git://github.com/stylus/stylus" + }, + "main": "./index.js", + "browserify": "./lib/browserify.js", + "engines": { + "node": "*" + }, + "bin": { + "stylus": "./bin/stylus" + }, + "scripts": { + "prepublish": "npm prune", + "test": "mocha test/ test/middleware/ --require chai --bail --check-leaks --reporter dot" + }, + "dependencies": { + "css": "^3.0.0", + "debug": "^4.3.2", + "glob": "^7.1.6", + "sax": "~1.2.4", + "source-map": "^0.7.3" + }, + "devDependencies": { + "chai": "^4.3.6", + "mocha": "^9.2.0" + }, + "bugs": { + "url": "https://github.com/stylus/stylus/issues" + }, + "homepage": "https://github.com/stylus/stylus", + "directories": { + "doc": "docs", + "example": "examples", + "test": "test" + }, + "license": "MIT" +} diff --git a/test/yarn/package.json b/test/yarn/package.json new file mode 100644 index 000000000..b8620c27c --- /dev/null +++ b/test/yarn/package.json @@ -0,0 +1,6 @@ +{ + "name": "yarn-regression", + "dependencies": { + "stylus": "file:../../" + } +}