Skip to content

Commit

Permalink
feat: introduce svgConfig option
Browse files Browse the repository at this point in the history
  • Loading branch information
farnabaz committed Sep 2, 2019
1 parent 7aca723 commit 8016593
Show file tree
Hide file tree
Showing 4 changed files with 105 additions and 63 deletions.
38 changes: 37 additions & 1 deletion demo/nuxt.config.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,34 @@
const path = require('path')
const cleanupIDs = require('svgo/plugins/cleanupIDs')
const removeAttrs = require('svgo/plugins/removeAttrs')
const removeDimensions = require('svgo/plugins/removeDimensions')
const removeViewBox = require('svgo/plugins/removeViewBox')
const inlineStyles = require('svgo/plugins/inlineStyles')

function defaultPlugins () {
// Enable removeAttrs plugin
// Remove id attribute to prevent conflict with our id
removeAttrs.active = true
removeAttrs.params.attrs = 'svg:id'

// Disable removeViewBox plugin and enable removeDimensions
// Keep viewBox and remove width & height attribute
removeViewBox.active = false
removeDimensions.active = true

// Make all styles inline
// By definition, a defs sprite is not usable as a CSS sprite
inlineStyles.active = true
inlineStyles.params.onlyMatchedOnce = false

return [
removeDimensions,
cleanupIDs,
removeAttrs,
removeViewBox,
inlineStyles
]
}
module.exports = {
srcDir: path.resolve(__dirname),
rootDir: path.resolve(__dirname, '..'),
Expand All @@ -13,5 +42,12 @@ module.exports = {

modules: [
'../lib/module.js'
]
],
svgSprite: {
svgoConfig () {
return {
plugins: defaultPlugins()
}
}
}
}
15 changes: 10 additions & 5 deletions lib/module.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,14 @@ import consola from 'consola'
import { mkdirp, writeFile } from 'fs-extra'
import chokidar from 'chokidar'
import { generateSprite, invalidateIcon, invalidateSprite, addIcon } from './utils'
import Svg from './svg'

const logger = consola.withScope('@nuxtjs/svg-sprite')
const DEFAULTS = {
input: '~/assets/sprite/svg',
output: '~/assets/sprite/gen',
defaultSprite: 'icons',
svgoConfig: null,
elementClass: 'icon',
spriteClassPrefix: 'sprite-'
}
Expand All @@ -26,6 +28,9 @@ export default async function module (moduleOptions) {
options.input = resolve(options.input)
options.output = resolve(options.output)

const config = typeof options.svgoConfig === 'function' ? options.svgoConfig() : options.svgoConfig
options.svg = new Svg(config)

await setupHooks.call(this, options)

// Register plugin
Expand All @@ -45,16 +50,16 @@ function watchFiles (options) {
logger.info(`Watching ${options._input} for new icons`)

// Watch for new icons
filesWatcher.on('add', file => addIcon(file, options))
filesWatcher.on('add', file => addIcon(options.svg, file, options))

// Keep eye on current icons
filesWatcher.on('change', file => addIcon(file, options))
filesWatcher.on('change', file => addIcon(options.svg, file, options))

// Pray for lost icon
filesWatcher.on('unlink', file => invalidateIcon(file, options))
filesWatcher.on('unlink', file => invalidateIcon(options.svg, file, options))

// Pray for lost directory
filesWatcher.on('unlinkDir', file => invalidateSprite(file, options))
filesWatcher.on('unlinkDir', file => invalidateSprite(options.svg, file, options))
}
}

Expand All @@ -71,7 +76,7 @@ function setupHooks (options) {
this.nuxt.hook('build:before', async () => {
await init.call(this, options)

await generateSprite(options)
await generateSprite(options.svg, options)
})

this.nuxt.hook('build:done', () => {
Expand Down
57 changes: 34 additions & 23 deletions lib/svg.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,48 +6,57 @@ import removeDimensions from 'svgo/plugins/removeDimensions'
import removeViewBox from 'svgo/plugins/removeViewBox'
import inlineStyles from 'svgo/plugins/inlineStyles'

// Enable removeAttrs plugin
// Remove id attribute to prevent conflict with our id
removeAttrs.active = true
removeAttrs.params.attrs = 'svg:id'
function defaultPlugins () {
// Enable removeAttrs plugin
// Remove id attribute to prevent conflict with our id
removeAttrs.active = true
removeAttrs.params.attrs = 'svg:id'

// Disable removeViewBox plugin and enable removeDimensions
// Keep viewBox and remove width & height attribute
removeViewBox.active = false
removeDimensions.active = true
// Disable removeViewBox plugin and enable removeDimensions
// Keep viewBox and remove width & height attribute
removeViewBox.active = false
removeDimensions.active = true

// Make all styles inline
// By definition, a defs sprite is not usable as a CSS sprite
inlineStyles.active = true
inlineStyles.params.onlyMatchedOnce = false
// Make all styles inline
// By definition, a defs sprite is not usable as a CSS sprite
inlineStyles.active = true
inlineStyles.params.onlyMatchedOnce = false

const svgOptimizer = new Svgo({
pluging: [
return [
removeDimensions,
cleanupIDs,
removeAttrs,
removeViewBox,
inlineStyles
]
})
}

function Svg (config) {
if (config === null) {
config = {
plugins: defaultPlugins()
}
}
this.svgOptimizer = new Svgo(config)
}

export async function writeSVG (path, content) {
Svg.prototype.writeSVG = async function (path, content) {
const result = await fs.writeFile(path, content, { flag: 'w' })
return result
}

export async function readSVG (path) {
Svg.prototype.readSVG = async function (path) {
const result = await fs.readFile(path)
return result
}

export async function optimizeSVG (name, content) {
Svg.prototype.optimizeSVG = async function (name, content) {
cleanupIDs.params.prefix = `${name}-`
const $data = await svgOptimizer.optimize(content)
const $data = await this.svgOptimizer.optimize(content)
return $data.data
}

export function convertToSymbol (name, content) {
Svg.prototype.convertToSymbol = function (name, content) {
const $data = content
.replace('<svg', `<symbol id="i-${name}"`)
.replace('</svg>', '</symbol>')
Expand All @@ -56,18 +65,18 @@ export function convertToSymbol (name, content) {
return $data
}

export function extractDefs (content) {
Svg.prototype.extractDefs = function (content) {
const $data = content
.match(/<defs>(.+)<\/defs>/)

return $data ? $data[1] : ''
}

export function isSVGFile (file) {
Svg.prototype.isSVGFile = function (file) {
return file.match(/.*\.svg$/)
}

export function wrap (content, defs) {
Svg.prototype.wrap = function (content, defs) {
return '<?xml version="1.0" encoding="UTF-8"?>\n' +
'<svg version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">\n' +
'<defs>\n' +
Expand All @@ -76,3 +85,5 @@ export function wrap (content, defs) {
content +
'\n</svg>'
}

export default Svg
58 changes: 24 additions & 34 deletions lib/utils.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,6 @@ import fs from 'fs-extra'
import consola from 'consola'
import chalk from 'chalk'

import {
writeSVG,
readSVG,
optimizeSVG,
convertToSymbol,
isSVGFile,
wrap,
extractDefs
} from './svg'

const logger = consola.withScope('@nuxtjs/svg-sprite')
const sprites = {}

Expand All @@ -27,11 +17,11 @@ function registerSymbol (sprite, symbol) {
sprites[sprite].symbols[symbol.name] = symbol
}

async function newIcon (file, sprite, name) {
const rawContent = await readSVG(file)
const optimizeContent = await optimizeSVG(name, rawContent)
const symbol = await convertToSymbol(name, optimizeContent)
const defs = await extractDefs(optimizeContent)
async function newIcon (svgMaster, file, sprite, name) {
const rawContent = await svgMaster.readSVG(file)
const optimizeContent = await svgMaster.optimizeSVG(name, rawContent)
const symbol = await svgMaster.convertToSymbol(name, optimizeContent)
const defs = await svgMaster.extractDefs(optimizeContent)

await registerSymbol(sprite, {
name,
Expand All @@ -41,7 +31,7 @@ async function newIcon (file, sprite, name) {
})
}

async function createSprite (name, source) {
async function createSprite (svgMaster, name, source) {
const files = await fs.readdir(source)

if (!sprites[name]) {
Expand All @@ -54,19 +44,19 @@ async function createSprite (name, source) {
const path = join(source, file)
const iconName = generateName(file)

if (isSVGFile(file) && !sprites[name].symbols[iconName]) {
await newIcon(path, name, iconName)
if (svgMaster.isSVGFile(file) && !sprites[name].symbols[iconName]) {
await newIcon(svgMaster, path, name, iconName)
}
}
}

async function writeSprites (directory) {
async function writeSprites (svgMaster, directory) {
for (const name in sprites) {
await writeSprite(name, directory)
await writeSprite(svgMaster, name, directory)
}
}

async function writeSprite (name, directory) {
async function writeSprite (svgMaster, name, directory) {
if (!sprites[name]) {
return
}
Expand All @@ -80,15 +70,15 @@ async function writeSprite (name, directory) {
.filter(d => Boolean(d))
.join('\n')

const svg = wrap(symbols, defs)
const svg = svgMaster.wrap(symbols, defs)

await writeSVG(
await svgMaster.writeSVG(
join(directory, `${name}.svg`),
svg
)
}

export async function addIcon (file, { input, output, defaultSprite }) {
export async function addIcon (svgMaster, file, { input, output, defaultSprite }) {
const path = file.replace(input + sep, '')
const arr = path.split(sep)
const sprite = arr.length === 2 ? arr[0] : defaultSprite
Expand All @@ -101,17 +91,17 @@ export async function addIcon (file, { input, output, defaultSprite }) {
}
}

await newIcon(file, sprite, iconName)
await newIcon(svgMaster, file, sprite, iconName)

await writeSprite(sprite, output)
await writeSprite(svgMaster, sprite, output)
logger.log({
type: 'added',
message: `SVG icon ${chalk.bold(sprite + '/' + iconName)} added`,
icon: chalk.green.bold('+')
})
}

export async function invalidateIcon (file, { input, output, defaultSprite }) {
export async function invalidateIcon (svgMaster, file, { input, output, defaultSprite }) {
const path = file.replace(input + sep, '')
const arr = path.split(sep)
const sprite = arr.length === 2 ? arr[0] : defaultSprite
Expand All @@ -124,15 +114,15 @@ export async function invalidateIcon (file, { input, output, defaultSprite }) {
await fs.unlink(spriteFile)
}

await writeSprite(sprite, output)
await writeSprite(svgMaster, sprite, output)
logger.log({
type: 'removed',
message: `SVG icon ${chalk.bold(sprite + '/' + iconName)} removed`,
icon: chalk.red.bold('-')
})
}

export async function invalidateSprite (file, { input, output }) {
export async function invalidateSprite (svgMaster, file, { input, output }) {
const arr = file.replace(input + sep, '').split(sep)
const sprite = arr[arr.length - 1]

Expand All @@ -144,24 +134,24 @@ export async function invalidateSprite (file, { input, output }) {
}
}

export async function generateSprite ({ input, output, defaultSprite }) {
export async function generateSprite (svgMaster, { input, output, defaultSprite }) {
const files = await fs.readdir(input)
let hasDefaultSprite = false

for (const file of files) {
const source = join(input, file)
if (isSVGFile(file)) {
if (svgMaster.isSVGFile(file)) {
hasDefaultSprite = true
}
const stat = await fs.lstat(source)
if (stat.isDirectory()) {
await createSprite(file, source)
await createSprite(svgMaster, file, source)
}
}

if (hasDefaultSprite) {
await createSprite(defaultSprite, input)
await createSprite(svgMaster, defaultSprite, input)
}

await writeSprites(output)
await writeSprites(svgMaster, output)
}

0 comments on commit 8016593

Please sign in to comment.