mirror of
https://github.com/tailwindlabs/tailwindcss.git
synced 2025-12-08 21:36:08 +00:00
commit
4fc519f9f5
@ -1,21 +1,53 @@
|
||||
import { spawnSync } from 'child_process'
|
||||
import fs from 'fs'
|
||||
import path from 'path'
|
||||
|
||||
function runCli(task, options) {
|
||||
return spawnSync('node', [`${path.join(process.cwd(), 'lib/cli.js')}`, `${task}`, ...options])
|
||||
}
|
||||
import cli from '../src/cli/main'
|
||||
import * as constants from '../src/cli/constants'
|
||||
import * as utils from '../src/cli/utils'
|
||||
|
||||
function pathToFixture(fixture) {
|
||||
return path.resolve(`${__dirname}/fixtures/${fixture}`)
|
||||
}
|
||||
describe('cli', () => {
|
||||
const inputCssPath = path.resolve(__dirname, 'fixtures/tailwind-input.css')
|
||||
const customConfigPath = path.resolve(__dirname, 'fixtures/custom-config.js')
|
||||
|
||||
function readFixture(fixture) {
|
||||
return fs.readFileSync(pathToFixture(fixture), 'utf8')
|
||||
}
|
||||
beforeEach(() => {
|
||||
console.log = jest.fn()
|
||||
process.stdout.write = jest.fn()
|
||||
utils.writeFile = jest.fn()
|
||||
})
|
||||
|
||||
test('stdout only contains processed output', () => {
|
||||
const expected = readFixture('tailwind-cli-output.css')
|
||||
const result = runCli('build', [pathToFixture('tailwind-cli-input.css')])
|
||||
expect(result.stdout.toString()).toEqual(expected)
|
||||
describe('init', () => {
|
||||
it('creates a Tailwind config file', () => {
|
||||
cli(['init']).then(() => {
|
||||
expect(utils.writeFile.mock.calls[0][0]).toEqual(constants.defaultConfigFile)
|
||||
expect(utils.writeFile.mock.calls[0][1]).toContain('defaultConfig')
|
||||
})
|
||||
})
|
||||
|
||||
it('creates a Tailwind config file in a custom location', () => {
|
||||
cli(['init', 'custom.js']).then(() => {
|
||||
expect(utils.writeFile.mock.calls[0][0]).toEqual('custom.js')
|
||||
expect(utils.writeFile.mock.calls[0][1]).toContain('defaultConfig')
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
describe('build', () => {
|
||||
it('compiles CSS file', () => {
|
||||
cli(['build', inputCssPath]).then(() => {
|
||||
expect(process.stdout.write.mock.calls[0][0]).toContain('.example')
|
||||
})
|
||||
})
|
||||
|
||||
it('compiles CSS file using custom configuration', () => {
|
||||
cli(['build', inputCssPath, '--config', customConfigPath]).then(() => {
|
||||
expect(process.stdout.write.mock.calls[0][0]).toContain('400px')
|
||||
})
|
||||
})
|
||||
|
||||
it('creates compiled CSS file', () => {
|
||||
cli(['build', inputCssPath, '--output', 'output.css']).then(() => {
|
||||
expect(utils.writeFile.mock.calls[0][0]).toEqual('output.css')
|
||||
expect(utils.writeFile.mock.calls[0][1]).toContain('.example')
|
||||
})
|
||||
})
|
||||
})
|
||||
})
|
||||
|
||||
@ -3,7 +3,7 @@ import postcss from 'postcss'
|
||||
import tailwind from '../src/index'
|
||||
|
||||
test('it uses the values from the custom config file', () => {
|
||||
return postcss([tailwind(path.resolve(`${__dirname}/fixtures/customConfig.js`))])
|
||||
return postcss([tailwind(path.resolve(`${__dirname}/fixtures/custom-config.js`))])
|
||||
.process(
|
||||
`
|
||||
@responsive {
|
||||
|
||||
@ -1,3 +0,0 @@
|
||||
body {
|
||||
color: green;
|
||||
}
|
||||
@ -1,3 +0,0 @@
|
||||
body {
|
||||
color: green;
|
||||
}
|
||||
@ -24,7 +24,6 @@
|
||||
"test": "jest && eslint . && nsp check"
|
||||
},
|
||||
"devDependencies": {
|
||||
"autoprefixer": "^7.1.6",
|
||||
"babel-cli": "^6.6.5",
|
||||
"babel-core": "^6.7.2",
|
||||
"babel-jest": "^20.0.3",
|
||||
@ -43,16 +42,20 @@
|
||||
"rimraf": "^2.6.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"commander": "^2.11.0",
|
||||
"autoprefixer": "^7.1.6",
|
||||
"bytes": "^3.0.0",
|
||||
"chalk": "^2.4.1",
|
||||
"css.escape": "^1.5.1",
|
||||
"fs-extra": "^4.0.2",
|
||||
"lodash": "^4.17.5",
|
||||
"node-emoji": "^1.8.1",
|
||||
"perfectionist": "^2.4.0",
|
||||
"postcss": "^6.0.9",
|
||||
"postcss-functions": "^3.0.0",
|
||||
"postcss-js": "^1.0.1",
|
||||
"postcss-nested": "^3.0.0",
|
||||
"postcss-selector-parser": "^3.1.1"
|
||||
"postcss-selector-parser": "^3.1.1",
|
||||
"pretty-hrtime": "^1.0.3"
|
||||
},
|
||||
"browserslist": [
|
||||
"> 1%"
|
||||
|
||||
96
src/cli.js
96
src/cli.js
@ -1,96 +1,6 @@
|
||||
#!/usr/bin/env node
|
||||
/* eslint-disable no-process-exit */
|
||||
|
||||
import path from 'path'
|
||||
import fs from 'fs-extra'
|
||||
import tailwind from '..'
|
||||
import postcss from 'postcss'
|
||||
import process from 'process'
|
||||
import program from 'commander'
|
||||
import main from './cli/main'
|
||||
import * as utils from './cli/utils'
|
||||
|
||||
function writeStrategy(options) {
|
||||
if (options.output === undefined) {
|
||||
return output => {
|
||||
process.stdout.write(output)
|
||||
}
|
||||
}
|
||||
return output => {
|
||||
fs.outputFileSync(options.output, output)
|
||||
}
|
||||
}
|
||||
|
||||
function buildTailwind(inputFile, config, write) {
|
||||
console.warn('Building Tailwind!')
|
||||
|
||||
const input = fs.readFileSync(inputFile, 'utf8')
|
||||
|
||||
return postcss([tailwind(config)])
|
||||
.process(input, { from: inputFile })
|
||||
.then(result => {
|
||||
write(result.css)
|
||||
console.warn('Finished building Tailwind!')
|
||||
})
|
||||
.catch(error => console.error(error))
|
||||
}
|
||||
|
||||
const packageJson = require(path.resolve(__dirname, '../package.json'))
|
||||
|
||||
program.version(packageJson.version).usage('<command> [<args>]')
|
||||
|
||||
program
|
||||
.command('init [filename]')
|
||||
.usage('[options] [filename]')
|
||||
.action((filename = 'tailwind.js') => {
|
||||
let destination = path.resolve(filename)
|
||||
|
||||
if (!path.extname(filename).includes('.js')) {
|
||||
destination += '.js'
|
||||
}
|
||||
|
||||
if (fs.existsSync(destination)) {
|
||||
console.error(`Destination ${destination} already exists, aborting.`)
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
const output = fs.readFileSync(path.resolve(__dirname, '../defaultConfig.stub.js'), 'utf8')
|
||||
fs.outputFileSync(destination, output.replace('// let defaultConfig', 'let defaultConfig'))
|
||||
fs.outputFileSync(
|
||||
destination,
|
||||
output.replace("require('./plugins/container')", "require('tailwindcss/plugins/container')")
|
||||
)
|
||||
console.warn(`Generated Tailwind config: ${destination}`)
|
||||
process.exit()
|
||||
})
|
||||
|
||||
program
|
||||
.command('build')
|
||||
.usage('[options] <file ...>')
|
||||
.option('-c, --config [path]', 'Path to config file')
|
||||
.option('-o, --output [path]', 'Output file')
|
||||
.action((file, options) => {
|
||||
let inputFile = program.args[0]
|
||||
|
||||
if (!inputFile) {
|
||||
console.error('No input file given!')
|
||||
process.exit(1)
|
||||
}
|
||||
|
||||
buildTailwind(inputFile, options.config, writeStrategy(options)).then(() => {
|
||||
process.exit()
|
||||
})
|
||||
})
|
||||
|
||||
program
|
||||
.command('*', null, {
|
||||
noHelp: true,
|
||||
})
|
||||
.action(() => {
|
||||
program.help()
|
||||
})
|
||||
|
||||
program.parse(process.argv)
|
||||
|
||||
if (program.args.length === 0) {
|
||||
program.help()
|
||||
process.exit()
|
||||
}
|
||||
main(process.argv.slice(2)).catch(error => utils.die(error.stack))
|
||||
|
||||
143
src/cli/commands/build.js
Normal file
143
src/cli/commands/build.js
Normal file
@ -0,0 +1,143 @@
|
||||
import autoprefixer from 'autoprefixer'
|
||||
import bytes from 'bytes'
|
||||
import chalk from 'chalk'
|
||||
import postcss from 'postcss'
|
||||
import prettyHrtime from 'pretty-hrtime'
|
||||
|
||||
import tailwind from '../..'
|
||||
|
||||
import commands from '.'
|
||||
import * as emoji from '../emoji'
|
||||
import * as utils from '../utils'
|
||||
|
||||
export const usage = 'build <file> [options]'
|
||||
export const description = 'Compiles Tailwind CSS file.'
|
||||
|
||||
export const options = [
|
||||
{
|
||||
usage: '-o, --output <file>',
|
||||
description: 'Output file.',
|
||||
},
|
||||
{
|
||||
usage: '-c, --config <file>',
|
||||
description: 'Tailwind config file.',
|
||||
},
|
||||
]
|
||||
|
||||
export const optionMap = {
|
||||
output: ['output', 'o'],
|
||||
config: ['config', 'c'],
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the error message and stops the process.
|
||||
*
|
||||
* @param {...string} [msgs]
|
||||
*/
|
||||
function stop(...msgs) {
|
||||
utils.header()
|
||||
utils.error(...msgs)
|
||||
utils.die()
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints the error message and help for this command, then stops the process.
|
||||
*
|
||||
* @param {...string} [msgs]
|
||||
*/
|
||||
function stopWithHelp(...msgs) {
|
||||
utils.header()
|
||||
utils.error(...msgs)
|
||||
commands.help.forCommand(commands.build)
|
||||
utils.die()
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles CSS file.
|
||||
*
|
||||
* @param {string} inputFile
|
||||
* @param {string} configFile
|
||||
* @param {string} outputFile
|
||||
* @return {Promise}
|
||||
*/
|
||||
function build(inputFile, configFile, outputFile) {
|
||||
const css = utils.readFile(inputFile)
|
||||
|
||||
return new Promise((resolve, reject) => {
|
||||
postcss([tailwind(configFile), autoprefixer])
|
||||
.process(css, {
|
||||
from: inputFile,
|
||||
to: outputFile,
|
||||
})
|
||||
.then(resolve)
|
||||
.catch(reject)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles CSS file and writes it to stdout.
|
||||
*
|
||||
* @param {string} inputFile
|
||||
* @param {string} configFile
|
||||
* @param {string} outputFile
|
||||
* @return {Promise}
|
||||
*/
|
||||
function buildToStdout(inputFile, configFile, outputFile) {
|
||||
return build(inputFile, configFile, outputFile).then(result => process.stdout.write(result.css))
|
||||
}
|
||||
|
||||
/**
|
||||
* Compiles CSS file and writes it to a file.
|
||||
*
|
||||
* @param {string} inputFile
|
||||
* @param {string} configFile
|
||||
* @param {string} outputFile
|
||||
* @param {int[]} startTime
|
||||
* @return {Promise}
|
||||
*/
|
||||
function buildToFile(inputFile, configFile, outputFile, startTime) {
|
||||
utils.header()
|
||||
utils.log()
|
||||
utils.log(emoji.go, 'Building...', chalk.bold.cyan(inputFile))
|
||||
|
||||
return build(inputFile, configFile, outputFile).then(result => {
|
||||
utils.writeFile(outputFile, result.css)
|
||||
|
||||
const prettyTime = prettyHrtime(process.hrtime(startTime))
|
||||
|
||||
utils.log()
|
||||
utils.log(emoji.yes, 'Finished in', chalk.bold.magenta(prettyTime))
|
||||
utils.log(emoji.pack, 'Size:', chalk.bold.magenta(bytes(result.css.length)))
|
||||
utils.log(emoji.disk, 'Saved to', chalk.bold.cyan(outputFile))
|
||||
utils.footer()
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the command.
|
||||
*
|
||||
* @param {string[]} cliParams
|
||||
* @param {object} cliOptions
|
||||
* @return {Promise}
|
||||
*/
|
||||
export function run(cliParams, cliOptions) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const startTime = process.hrtime()
|
||||
const inputFile = cliParams[0]
|
||||
const configFile = cliOptions.config && cliOptions.config[0]
|
||||
const outputFile = cliOptions.output && cliOptions.output[0]
|
||||
|
||||
!inputFile && stopWithHelp('CSS file is required.')
|
||||
!utils.exists(inputFile) && stop(chalk.bold.magenta(inputFile), 'does not exist.')
|
||||
|
||||
configFile &&
|
||||
!utils.exists(configFile) &&
|
||||
stop(chalk.bold.magenta(configFile), 'does not exist.')
|
||||
|
||||
const buildPromise = outputFile
|
||||
? buildToFile(inputFile, configFile, outputFile, startTime)
|
||||
: buildToStdout(inputFile, configFile, outputFile)
|
||||
|
||||
buildPromise.then(resolve).catch(reject)
|
||||
})
|
||||
}
|
||||
85
src/cli/commands/help.js
Normal file
85
src/cli/commands/help.js
Normal file
@ -0,0 +1,85 @@
|
||||
import chalk from 'chalk'
|
||||
import { forEach, map, padEnd } from 'lodash'
|
||||
|
||||
import commands from '.'
|
||||
import * as constants from '../constants'
|
||||
import * as utils from '../utils'
|
||||
|
||||
export const usage = 'help [command]'
|
||||
export const description = 'More information about the command.'
|
||||
|
||||
const PADDING_SIZE = 3
|
||||
|
||||
/**
|
||||
* Prints general help.
|
||||
*/
|
||||
export function forApp() {
|
||||
const pad = Math.max(...map(commands, 'usage.length')) + PADDING_SIZE
|
||||
|
||||
utils.log()
|
||||
utils.log('Usage:')
|
||||
utils.log(' ', chalk.bold(constants.cli + ' <command> [options]'))
|
||||
utils.log()
|
||||
utils.log('Commands:')
|
||||
forEach(commands, command => {
|
||||
utils.log(' ', chalk.bold(padEnd(command.usage, pad)), command.description)
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints help for a command.
|
||||
*
|
||||
* @param {object} command
|
||||
*/
|
||||
export function forCommand(command) {
|
||||
utils.log()
|
||||
utils.log('Usage:')
|
||||
utils.log(' ', chalk.bold(constants.cli, command.usage))
|
||||
utils.log()
|
||||
utils.log('Description:')
|
||||
utils.log(' ', chalk.bold(command.description))
|
||||
|
||||
if (command.options) {
|
||||
const pad = Math.max(...map(command.options, 'usage.length')) + PADDING_SIZE
|
||||
|
||||
utils.log()
|
||||
utils.log('Options:')
|
||||
forEach(command.options, option => {
|
||||
utils.log(' ', chalk.bold(padEnd(option.usage, pad)), option.description)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints invalid command error and general help. Kills the process.
|
||||
*
|
||||
* @param {string} commandName
|
||||
*/
|
||||
export function invalidCommand(commandName) {
|
||||
utils.error('Invalid command:', chalk.bold.magenta(commandName))
|
||||
forApp()
|
||||
utils.die()
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs the command.
|
||||
*
|
||||
* @param {string[]} cliParams
|
||||
* @return {Promise}
|
||||
*/
|
||||
export function run(cliParams) {
|
||||
return new Promise(resolve => {
|
||||
utils.header()
|
||||
|
||||
const commandName = cliParams[0]
|
||||
const command = commands[commandName]
|
||||
|
||||
!commandName && forApp()
|
||||
commandName && command && forCommand(command)
|
||||
commandName && !command && invalidCommand(commandName)
|
||||
|
||||
utils.footer()
|
||||
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
5
src/cli/commands/index.js
Normal file
5
src/cli/commands/index.js
Normal file
@ -0,0 +1,5 @@
|
||||
import * as help from './help'
|
||||
import * as init from './init'
|
||||
import * as build from './build'
|
||||
|
||||
export default { help, init, build }
|
||||
39
src/cli/commands/init.js
Normal file
39
src/cli/commands/init.js
Normal file
@ -0,0 +1,39 @@
|
||||
import chalk from 'chalk'
|
||||
|
||||
import * as constants from '../constants'
|
||||
import * as emoji from '../emoji'
|
||||
import * as utils from '../utils'
|
||||
|
||||
export const usage = 'init [file]'
|
||||
export const description =
|
||||
'Creates Tailwind config file. Default: ' + chalk.bold.magenta(constants.defaultConfigFile)
|
||||
|
||||
/**
|
||||
* Runs the command.
|
||||
*
|
||||
* @param {string[]} cliParams
|
||||
* @return {Promise}
|
||||
*/
|
||||
export function run(cliParams) {
|
||||
return new Promise(resolve => {
|
||||
utils.header()
|
||||
|
||||
const file = cliParams[0] || constants.defaultConfigFile
|
||||
|
||||
utils.exists(file) && utils.die(chalk.bold.magenta(file), 'already exists.')
|
||||
|
||||
const stub = utils
|
||||
.readFile(constants.configStubFile)
|
||||
.replace('// let defaultConfig', 'let defaultConfig')
|
||||
.replace("require('./plugins/container')", "require('tailwindcss/plugins/container')")
|
||||
|
||||
utils.writeFile(file, stub)
|
||||
|
||||
utils.log()
|
||||
utils.log(emoji.yes, 'Created Tailwind config file:', chalk.bold.magenta(file))
|
||||
|
||||
utils.footer()
|
||||
|
||||
resolve()
|
||||
})
|
||||
}
|
||||
5
src/cli/constants.js
Normal file
5
src/cli/constants.js
Normal file
@ -0,0 +1,5 @@
|
||||
import path from 'path'
|
||||
|
||||
export const cli = 'tailwind'
|
||||
export const defaultConfigFile = 'tailwind.js'
|
||||
export const configStubFile = path.resolve(__dirname, '../../defaultConfig.stub.js')
|
||||
7
src/cli/emoji.js
Normal file
7
src/cli/emoji.js
Normal file
@ -0,0 +1,7 @@
|
||||
import { get } from 'node-emoji'
|
||||
|
||||
export const yes = get('white_check_mark')
|
||||
export const no = get('no_entry_sign')
|
||||
export const go = get('rocket')
|
||||
export const pack = get('package')
|
||||
export const disk = get('floppy_disk')
|
||||
22
src/cli/main.js
Normal file
22
src/cli/main.js
Normal file
@ -0,0 +1,22 @@
|
||||
import commands from './commands'
|
||||
import * as utils from './utils'
|
||||
|
||||
/**
|
||||
* CLI application entrypoint.
|
||||
*
|
||||
* @param {string[]} cliArgs
|
||||
* @return {Promise}
|
||||
*/
|
||||
export default function run(cliArgs) {
|
||||
return new Promise((resolve, reject) => {
|
||||
const params = utils.parseCliParams(cliArgs)
|
||||
const command = commands[params[0]]
|
||||
const options = command ? utils.parseCliOptions(cliArgs, command.optionMap) : {}
|
||||
|
||||
const commandPromise = command
|
||||
? command.run(params.slice(1), options)
|
||||
: commands.help.run(params)
|
||||
|
||||
commandPromise.then(resolve).catch(reject)
|
||||
})
|
||||
}
|
||||
123
src/cli/utils.js
Normal file
123
src/cli/utils.js
Normal file
@ -0,0 +1,123 @@
|
||||
import chalk from 'chalk'
|
||||
import { ensureFileSync, existsSync, outputFileSync, readFileSync } from 'fs-extra'
|
||||
import { findKey, mapValues, trimStart } from 'lodash'
|
||||
|
||||
import * as emoji from './emoji'
|
||||
import packageJson from '../../package.json'
|
||||
|
||||
/**
|
||||
* Gets CLI parameters.
|
||||
*
|
||||
* @param {string[]} cliArgs
|
||||
* @return {string[]}
|
||||
*/
|
||||
export function parseCliParams(cliArgs) {
|
||||
const firstOptionIndex = cliArgs.findIndex(cliArg => cliArg.startsWith('-'))
|
||||
|
||||
return firstOptionIndex > -1 ? cliArgs.slice(0, firstOptionIndex) : cliArgs
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets mapped CLI options.
|
||||
*
|
||||
* @param {string[]} cliArgs
|
||||
* @param {object} [optionMap]
|
||||
* @return {object}
|
||||
*/
|
||||
export function parseCliOptions(cliArgs, optionMap = {}) {
|
||||
let options = {}
|
||||
let currentOption = []
|
||||
|
||||
cliArgs.forEach(cliArg => {
|
||||
const option = cliArg.startsWith('-') && trimStart(cliArg, '-').toLowerCase()
|
||||
const resolvedOption = findKey(optionMap, aliases => aliases.includes(option))
|
||||
|
||||
if (resolvedOption) {
|
||||
currentOption = options[resolvedOption] || (options[resolvedOption] = [])
|
||||
} else if (option) {
|
||||
currentOption = []
|
||||
} else {
|
||||
currentOption.push(cliArg)
|
||||
}
|
||||
})
|
||||
|
||||
return { ...mapValues(optionMap, () => undefined), ...options }
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints messages to console.
|
||||
*
|
||||
* @param {...string} [msgs]
|
||||
*/
|
||||
export function log(...msgs) {
|
||||
console.log(' ', ...msgs)
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints application header to console.
|
||||
*/
|
||||
export function header() {
|
||||
log()
|
||||
log(chalk.bold(packageJson.name), chalk.bold.cyan(packageJson.version))
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints application footer to console.
|
||||
*/
|
||||
export function footer() {
|
||||
log()
|
||||
}
|
||||
|
||||
/**
|
||||
* Prints error messages to console.
|
||||
*
|
||||
* @param {...string} [msgs]
|
||||
*/
|
||||
export function error(...msgs) {
|
||||
log()
|
||||
console.error(' ', emoji.no, chalk.bold.red(msgs.join(' ')))
|
||||
}
|
||||
|
||||
/**
|
||||
* Kills the process. Optionally prints error messages to console.
|
||||
*
|
||||
* @param {...string} [msgs]
|
||||
*/
|
||||
export function die(...msgs) {
|
||||
msgs.length && error(...msgs)
|
||||
footer()
|
||||
process.exit(1) // eslint-disable-line
|
||||
}
|
||||
|
||||
/**
|
||||
* Checks if path exists.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {boolean}
|
||||
*/
|
||||
export function exists(path) {
|
||||
return existsSync(path)
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets file content.
|
||||
*
|
||||
* @param {string} path
|
||||
* @return {string}
|
||||
*/
|
||||
export function readFile(path) {
|
||||
return readFileSync(path, 'utf-8')
|
||||
}
|
||||
|
||||
/**
|
||||
* Writes content to file.
|
||||
*
|
||||
* @param {string} path
|
||||
* @param {string} content
|
||||
* @return {string}
|
||||
*/
|
||||
export function writeFile(path, content) {
|
||||
ensureFileSync(path)
|
||||
|
||||
return outputFileSync(path, content)
|
||||
}
|
||||
Loading…
x
Reference in New Issue
Block a user