From 2389b2486d12cc03e0544e4b9e89195226812984 Mon Sep 17 00:00:00 2001 From: mathieudutour Date: Fri, 17 Aug 2018 16:07:39 -0500 Subject: [PATCH] handle custom plugins --- README.md | 19 + .../Contents/Sketch/plugin.js | 581 +++++++++++++++++- package-lock.json | 5 + package.json | 1 + src/plugin.js | 36 +- 5 files changed, 623 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index 4442b97..5705b51 100644 --- a/README.md +++ b/README.md @@ -25,6 +25,7 @@ If for some reason you’re not happy with the default settings we’ve chosen, - `indent`: Indent number when pretty printing SVGs - `multipass`: Run the optimization 10 times - `floatPrecision`: The precision of the numbers to use in all the plugins +- `debug`: Print some logs to help debug the SVGO configuration ### SVGO Plugins @@ -592,6 +593,24 @@ Sorts element attributes to improve readability. On by default in SVGO Compressor. +### Custom SVGO Plugins + +__requires Sketch >= 52__ + +You can specify custom SVGO plugins by giving a relative path to a JS file instead of a name: + +```diff +... ++ { ++ "path": "./SVGO-plugins/sRGB-blur.js", ++ "enabled": true, ++ "params": {} ++ } +``` + +You can find a list of custom SVGO plugins that the community created: +- [sRGB-blur](https://gist.github.com/mathieudutour/73309f6fb7fb6e7b830e20e43ee8f850) (see #40) + ## Acknowledgements We would like to thank: diff --git a/SVGO Compressor.sketchplugin/Contents/Sketch/plugin.js b/SVGO Compressor.sketchplugin/Contents/Sketch/plugin.js index c326599..712d034 100644 --- a/SVGO Compressor.sketchplugin/Contents/Sketch/plugin.js +++ b/SVGO Compressor.sketchplugin/Contents/Sketch/plugin.js @@ -23310,6 +23310,10 @@ var _getConfig = __webpack_require__(163); var _getConfig2 = _interopRequireDefault(_getConfig); +var _path = __webpack_require__(478); + +var _path2 = _interopRequireDefault(_path); + var _fs = __webpack_require__(32); var _fs2 = _interopRequireDefault(_fs); @@ -23335,8 +23339,6 @@ var _ui2 = _interopRequireDefault(_ui); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { 'default': obj }; } function openSettings() { - var config = (0, _getConfig2['default'])(); - // Plugin was run from the menu, so let's open the settings window var response = _dialog2['default'].showMessageBox({ buttons: ['Edit SVGO Settings…', 'Reset SVGO Settings', 'Cancel'], @@ -23367,11 +23369,32 @@ function compress(context) { return; } var plugin = _svgoPlugins2['default'][item.name]; + if (item.path) { + try { + var loadedPlugin = coscript.require(_path2['default'].join(String(MSPluginManager.mainPluginsFolderURL().path()), item.path)); + + // loadedPlugin is an NSDictionary so if we try to set something on it, + // it will crash. Instead we move the values to a proper JS object. + var keys = Object.keys(loadedPlugin); + plugin = {}; + Object.keys(loadedPlugin).forEach(function (k) { + plugin[k] = loadedPlugin[k]; + }); + if (loadedPlugin.params) { + plugin.params = {}; + Object.keys(loadedPlugin.params).forEach(function (k) { + plugin.params[k] = loadedPlugin.params[k]; + }); + } + } catch (err) { + log(err); + } + } if (!plugin) { - log('Plugin not found: ' + item.name); + log('Plugin not found: ' + (item.name || item.path)); return; } - log('Enabled plugin: ' + item.name); + if (svgoJSON.debug) log('Enabled plugin: ' + (item.name || item.path)); plugin.pluginName = item.name; plugin.active = true; if (plugin.params) { @@ -23381,7 +23404,7 @@ function compress(context) { if (floatPrecision && 'floatPrecision' in plugin.params) { plugin.params.floatPrecision = floatPrecision; } - log('—› default params: ' + JSON.stringify(plugin.params, null, 2)); + if (svgoJSON.debug) log('—› default params: ' + JSON.stringify(plugin.params, null, 2)); } if (item.params != null) { if (typeof plugin.params === 'undefined') { @@ -23390,7 +23413,7 @@ function compress(context) { for (var attrname in item.params) { plugin.params[attrname] = item.params[attrname]; } - log('—› resulting params: ' + JSON.stringify(plugin.params, null, 2)); + if (svgoJSON.debug) log('—› resulting params: ' + JSON.stringify(plugin.params, null, 2)); } parsedSVGOPlugins.push([plugin]); }); @@ -23405,7 +23428,7 @@ function compress(context) { } if (filesToCompress.length > 0) { - log('Let‘s go…'); + if (svgoJSON.debug) log('Let‘s go…'); var originalTotalSize = 0; var compressedTotalSize = 0; if (typeof svgoJSON.full === 'undefined') { @@ -23442,7 +23465,7 @@ function compress(context) { if (plugin.pluginName == "cleanupIDs") { var parts = currentFile.split('/'); var prefix = parts[parts.length - 1].replace('.svg', '').replace(/\s+/g, '-').toLowerCase() + "-"; - log('Setting cleanupIDs prefix to: ' + prefix); + if (svgoJSON.debug) log('Setting cleanupIDs prefix to: ' + prefix); plugin.params['prefix'] = prefix; } }); @@ -52940,7 +52963,7 @@ module.exports = function restructRule(ast) { /* 413 */ /***/ (function(module, exports) { -module.exports = {"_args":[["csso@3.5.0","/Users/ale/Code/svgo-compressor"]],"_from":"csso@3.5.0","_id":"csso@3.5.0","_inBundle":false,"_integrity":"sha512-WtJjFP3ZsSdWhiZr4/k1B9uHPgYjFYnDxfbaJxk1hz5PDLIJ5BCRWkJqaztZ0DbP8d2ZIVwUPIJb2YmCwkPaMw==","_location":"/csso","_phantomChildren":{"mdn-data":"1.1.0","source-map":"0.5.7"},"_requested":{"type":"version","registry":true,"raw":"csso@3.5.0","name":"csso","escapedName":"csso","rawSpec":"3.5.0","saveSpec":null,"fetchSpec":"3.5.0"},"_requiredBy":["/svgo"],"_resolved":"https://registry.npmjs.org/csso/-/csso-3.5.0.tgz","_spec":"3.5.0","_where":"/Users/ale/Code/svgo-compressor","author":{"name":"Sergey Kryzhanovsky","email":"skryzhanovsky@ya.ru","url":"https://github.com/afelix"},"bugs":{"url":"https://github.com/css/csso/issues"},"dependencies":{"css-tree":"1.0.0-alpha.27"},"description":"CSS minifier with structural optimisations","devDependencies":{"browserify":"^13.0.0","coveralls":"^2.11.6","eslint":"^2.2.0","istanbul":"^0.4.2","jscs":"~2.10.0","mocha":"~2.4.2","package-json-versionify":"^1.0.4","source-map":"^0.5.6","uglify-js":"^2.6.1"},"engines":{"node":">=0.10.0"},"eslintConfig":{"env":{"node":true,"mocha":true,"es6":true},"rules":{"no-duplicate-case":2,"no-undef":2,"no-unused-vars":[2,{"vars":"all","args":"after-used"}]}},"files":["dist/csso-browser.js","lib","HISTORY.md","LICENSE","README.md"],"homepage":"https://github.com/css/csso","keywords":["css","compress","minifier","minify","optimise","optimisation","csstree"],"license":"MIT","main":"./lib/index","maintainers":[{"name":"Roman Dvornov","email":"rdvornov@gmail.com"}],"name":"csso","repository":{"type":"git","url":"git+https://github.com/css/csso.git"},"scripts":{"browserify":"browserify -t package-json-versionify --standalone csso lib/index.js | uglifyjs --compress --mangle -o dist/csso-browser.js","codestyle":"jscs lib test && eslint lib test","codestyle-and-test":"npm run codestyle && npm test","coverage":"istanbul cover _mocha -- -R dot","coveralls":"istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | coveralls","gh-pages":"git clone --depth=1 -b gh-pages https://github.com/css/csso.git .gh-pages && npm run browserify && cp dist/csso-browser.js .gh-pages/ && cd .gh-pages && git commit -am \"update\" && git push && cd .. && rm -rf .gh-pages","hydrogen":"node --trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces --redirect-code-traces-to=code.asm --trace_hydrogen_file=code.cfg --print-opt-code bin/csso --stat -o /dev/null","postpublish":"npm run gh-pages","prepublish":"npm run browserify","test":"mocha --reporter dot","travis":"npm run codestyle-and-test && npm run coveralls"},"version":"3.5.0"} +module.exports = {"_from":"csso@^3.5.0","_id":"csso@3.5.0","_inBundle":false,"_integrity":"sha512-WtJjFP3ZsSdWhiZr4/k1B9uHPgYjFYnDxfbaJxk1hz5PDLIJ5BCRWkJqaztZ0DbP8d2ZIVwUPIJb2YmCwkPaMw==","_location":"/csso","_phantomChildren":{"mdn-data":"1.1.0","source-map":"0.5.7"},"_requested":{"type":"range","registry":true,"raw":"csso@^3.5.0","name":"csso","escapedName":"csso","rawSpec":"^3.5.0","saveSpec":null,"fetchSpec":"^3.5.0"},"_requiredBy":["/svgo"],"_resolved":"https://registry.npmjs.org/csso/-/csso-3.5.0.tgz","_shasum":"acdbba5719e2c87bc801eadc032764b2e4b9d4e7","_spec":"csso@^3.5.0","_where":"/Users/mathieudutour/Projects/svgo-compressor/node_modules/svgo","author":{"name":"Sergey Kryzhanovsky","email":"skryzhanovsky@ya.ru","url":"https://github.com/afelix"},"bugs":{"url":"https://github.com/css/csso/issues"},"bundleDependencies":false,"dependencies":{"css-tree":"1.0.0-alpha.27"},"deprecated":false,"description":"CSS minifier with structural optimisations","devDependencies":{"browserify":"^13.0.0","coveralls":"^2.11.6","eslint":"^2.2.0","istanbul":"^0.4.2","jscs":"~2.10.0","mocha":"~2.4.2","package-json-versionify":"^1.0.4","source-map":"^0.5.6","uglify-js":"^2.6.1"},"engines":{"node":">=0.10.0"},"eslintConfig":{"env":{"node":true,"mocha":true,"es6":true},"rules":{"no-duplicate-case":2,"no-undef":2,"no-unused-vars":[2,{"vars":"all","args":"after-used"}]}},"files":["dist/csso-browser.js","lib","HISTORY.md","LICENSE","README.md"],"homepage":"https://github.com/css/csso","keywords":["css","compress","minifier","minify","optimise","optimisation","csstree"],"license":"MIT","main":"./lib/index","maintainers":[{"name":"Roman Dvornov","email":"rdvornov@gmail.com"}],"name":"csso","repository":{"type":"git","url":"git+https://github.com/css/csso.git"},"scripts":{"browserify":"browserify -t package-json-versionify --standalone csso lib/index.js | uglifyjs --compress --mangle -o dist/csso-browser.js","codestyle":"jscs lib test && eslint lib test","codestyle-and-test":"npm run codestyle && npm test","coverage":"istanbul cover _mocha -- -R dot","coveralls":"istanbul cover _mocha --report lcovonly -- -R dot && cat ./coverage/lcov.info | coveralls","gh-pages":"git clone --depth=1 -b gh-pages https://github.com/css/csso.git .gh-pages && npm run browserify && cp dist/csso-browser.js .gh-pages/ && cd .gh-pages && git commit -am \"update\" && git push && cd .. && rm -rf .gh-pages","hydrogen":"node --trace-hydrogen --trace-phase=Z --trace-deopt --code-comments --hydrogen-track-positions --redirect-code-traces --redirect-code-traces-to=code.asm --trace_hydrogen_file=code.cfg --print-opt-code bin/csso --stat -o /dev/null","postpublish":"npm run gh-pages","prepublish":"npm run browserify","test":"mocha --reporter dot","travis":"npm run codestyle-and-test && npm run coveralls"},"version":"3.5.0"} /***/ }), /* 414 */ @@ -60158,6 +60181,546 @@ exports['default'] = { module.exports = require("sketch/ui"); +/***/ }), +/* 478 */ +/***/ (function(module, exports) { + +// Copyright Joyent, Inc. and other Node contributors. +// +// Permission is hereby granted, free of charge, to any person obtaining a +// copy of this software and associated documentation files (the +// "Software"), to deal in the Software without restriction, including +// without limitation the rights to use, copy, modify, merge, publish, +// distribute, sublicense, and/or sell copies of the Software, and to permit +// persons to whom the Software is furnished to do so, subject to the +// following conditions: +// +// The above copyright notice and this permission notice shall be included +// in all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN +// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, +// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR +// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE +// USE OR OTHER DEALINGS IN THE SOFTWARE. + +// we only expose the posix implementation since Sketch only runs on macOS + +var CHAR_FORWARD_SLASH = 47 +var CHAR_DOT = 46 + +// Resolves . and .. elements in a path with directory names +function normalizeString(path, allowAboveRoot) { + var res = '' + var lastSegmentLength = 0 + var lastSlash = -1 + var dots = 0 + var code + for (var i = 0; i <= path.length; i += 1) { + if (i < path.length) code = path.charCodeAt(i) + else if (code === CHAR_FORWARD_SLASH) break + else code = CHAR_FORWARD_SLASH + if (code === CHAR_FORWARD_SLASH) { + if (lastSlash === i - 1 || dots === 1) { + // NOOP + } else if (lastSlash !== i - 1 && dots === 2) { + if ( + res.length < 2 || + lastSegmentLength !== 2 || + res.charCodeAt(res.length - 1) !== CHAR_DOT || + res.charCodeAt(res.length - 2) !== CHAR_DOT + ) { + if (res.length > 2) { + var lastSlashIndex = res.lastIndexOf('/') + if (lastSlashIndex !== res.length - 1) { + if (lastSlashIndex === -1) { + res = '' + lastSegmentLength = 0 + } else { + res = res.slice(0, lastSlashIndex) + lastSegmentLength = res.length - 1 - res.lastIndexOf('/') + } + lastSlash = i + dots = 0 + continue + } + } else if (res.length === 2 || res.length === 1) { + res = '' + lastSegmentLength = 0 + lastSlash = i + dots = 0 + continue + } + } + if (allowAboveRoot) { + if (res.length > 0) res += '/..' + else res = '..' + lastSegmentLength = 2 + } + } else { + if (res.length > 0) res += '/' + path.slice(lastSlash + 1, i) + else res = path.slice(lastSlash + 1, i) + lastSegmentLength = i - lastSlash - 1 + } + lastSlash = i + dots = 0 + } else if (code === CHAR_DOT && dots !== -1) { + ++dots + } else { + dots = -1 + } + } + return res +} + +function _format(sep, pathObject) { + var dir = pathObject.dir || pathObject.root + var base = + pathObject.base || (pathObject.name || '') + (pathObject.ext || '') + if (!dir) { + return base + } + if (dir === pathObject.root) { + return dir + base + } + return dir + sep + base +} + +function normalizePath(path) { + if (typeof path === 'string') { + return path + } + if (path && path.class && typeof path.class === 'function') { + const className = String(path.class()) + if (className === 'NSString') { + return String(path) + } else if (className === 'NSURL') { + return String(path.path()) + } + } + throw new Error('path should be a string') +} + +var posix = { + // path.resolve([from ...], to) + resolve: function resolve() { + var resolvedPath = '' + var resolvedAbsolute = false + var cwd + + for (var i = arguments.length - 1; i >= -1 && !resolvedAbsolute; i -= 1) { + var path + if (i >= 0) path = arguments[i] + else { + if (cwd === undefined) + cwd = posix.dirname(String(__command.script().URL().path()) || MSPluginManager.defaultPluginURL()) + path = cwd + } + + path = normalizePath(path) + + // Skip empty entries + if (path.length === 0) { + continue + } + + resolvedPath = path + '/' + resolvedPath + resolvedAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH + } + + // At this point the path should be resolved to a full absolute path, but + // handle relative paths to be safe (might happen when process.cwd() fails) + + // Normalize the path + resolvedPath = normalizeString(resolvedPath, !resolvedAbsolute) + + if (resolvedAbsolute) { + if (resolvedPath.length > 0) return '/' + resolvedPath + else return '/' + } else if (resolvedPath.length > 0) { + return resolvedPath + } else { + return '.' + } + }, + + normalize: function normalize(path) { + path = normalizePath(path) + + if (path.length === 0) return '.' + + var isAbsolute = path.charCodeAt(0) === CHAR_FORWARD_SLASH + var trailingSeparator = + path.charCodeAt(path.length - 1) === CHAR_FORWARD_SLASH + + // Normalize the path + path = normalizeString(path, !isAbsolute) + + if (path.length === 0 && !isAbsolute) path = '.' + if (path.length > 0 && trailingSeparator) path += '/' + + if (isAbsolute) return '/' + path + return path + }, + + isAbsolute: function isAbsolute(path) { + path = normalizePath(path) + return path.length > 0 && path.charCodeAt(0) === CHAR_FORWARD_SLASH + }, + + join: function join() { + if (arguments.length === 0) return '.' + var joined + for (var i = 0; i < arguments.length; i += 1) { + var arg = arguments[i] + arg = normalizePath(arg) + if (arg.length > 0) { + if (joined === undefined) joined = arg + else joined += '/' + arg + } + } + if (joined === undefined) return '.' + return posix.normalize(joined) + }, + + relative: function relative(from, to) { + from = normalizePath(from) + to = normalizePath(to) + + if (from === to) return '' + + from = posix.resolve(from) + to = posix.resolve(to) + + if (from === to) return '' + + // Trim any leading backslashes + var fromStart = 1 + for (; fromStart < from.length; fromStart += 1) { + if (from.charCodeAt(fromStart) !== CHAR_FORWARD_SLASH) break + } + var fromEnd = from.length + var fromLen = fromEnd - fromStart + + // Trim any leading backslashes + var toStart = 1 + for (; toStart < to.length; toStart += 1) { + if (to.charCodeAt(toStart) !== CHAR_FORWARD_SLASH) break + } + var toEnd = to.length + var toLen = toEnd - toStart + + // Compare paths to find the longest common path from root + var length = fromLen < toLen ? fromLen : toLen + var lastCommonSep = -1 + var i = 0 + for (; i <= length; i += 1) { + if (i === length) { + if (toLen > length) { + if (to.charCodeAt(toStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `from` is the exact base path for `to`. + // For example: from='/foo/bar'; to='/foo/bar/baz' + return to.slice(toStart + i + 1) + } else if (i === 0) { + // We get here if `from` is the root + // For example: from='/'; to='/foo' + return to.slice(toStart + i) + } + } else if (fromLen > length) { + if (from.charCodeAt(fromStart + i) === CHAR_FORWARD_SLASH) { + // We get here if `to` is the exact base path for `from`. + // For example: from='/foo/bar/baz'; to='/foo/bar' + lastCommonSep = i + } else if (i === 0) { + // We get here if `to` is the root. + // For example: from='/foo'; to='/' + lastCommonSep = 0 + } + } + break + } + var fromCode = from.charCodeAt(fromStart + i) + var toCode = to.charCodeAt(toStart + i) + if (fromCode !== toCode) break + else if (fromCode === CHAR_FORWARD_SLASH) lastCommonSep = i + } + + var out = '' + // Generate the relative path based on the path difference between `to` + // and `from` + for (i = fromStart + lastCommonSep + 1; i <= fromEnd; i += 1) { + if (i === fromEnd || from.charCodeAt(i) === CHAR_FORWARD_SLASH) { + if (out.length === 0) out += '..' + else out += '/..' + } + } + + // Lastly, append the rest of the destination (`to`) path that comes after + // the common path parts + if (out.length > 0) return out + to.slice(toStart + lastCommonSep) + else { + toStart += lastCommonSep + if (to.charCodeAt(toStart) === CHAR_FORWARD_SLASH) toStart += 1 + return to.slice(toStart) + } + }, + + toNamespacedPath: function toNamespacedPath(path) { + // Non-op on posix systems + return path + }, + + dirname: function dirname(path) { + path = normalizePath(path) + if (path.length === 0) return '.' + var code = path.charCodeAt(0) + var hasRoot = code === CHAR_FORWARD_SLASH + var end = -1 + var matchedSlash = true + for (var i = path.length - 1; i >= 1; i -= 1) { + code = path.charCodeAt(i) + if (code === CHAR_FORWARD_SLASH) { + if (!matchedSlash) { + end = i + break + } + } else { + // We saw the first non-path separator + matchedSlash = false + } + } + + if (end === -1) return hasRoot ? '/' : '.' + if (hasRoot && end === 1) return '//' + return path.slice(0, end) + }, + + basename: function basename(path, ext) { + if (ext !== undefined && typeof ext !== 'string') + throw new Error('ext should be a string') + path = normalizePath(path) + + var start = 0 + var end = -1 + var matchedSlash = true + var i + + if (ext !== undefined && ext.length > 0 && ext.length <= path.length) { + if (ext.length === path.length && ext === path) return '' + var extIdx = ext.length - 1 + var firstNonSlashEnd = -1 + for (i = path.length - 1; i >= 0; i -= 1) { + var code = path.charCodeAt(i) + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1 + break + } + } else { + if (firstNonSlashEnd === -1) { + // We saw the first non-path separator, remember this index in case + // we need it if the extension ends up not matching + matchedSlash = false + firstNonSlashEnd = i + 1 + } + if (extIdx >= 0) { + // Try to match the explicit extension + if (code === ext.charCodeAt(extIdx)) { + if (--extIdx === -1) { + // We matched the extension, so mark this as the end of our path + // component + end = i + } + } else { + // Extension does not match, so our result is the entire path + // component + extIdx = -1 + end = firstNonSlashEnd + } + } + } + } + + if (start === end) end = firstNonSlashEnd + else if (end === -1) end = path.length + return path.slice(start, end) + } else { + for (i = path.length - 1; i >= 0; --i) { + if (path.charCodeAt(i) === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + start = i + 1 + break + } + } else if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // path component + matchedSlash = false + end = i + 1 + } + } + + if (end === -1) return '' + return path.slice(start, end) + } + }, + + extname: function extname(path) { + path = normalizePath(path) + var startDot = -1 + var startPart = 0 + var end = -1 + var matchedSlash = true + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + var preDotState = 0 + for (var i = path.length - 1; i >= 0; --i) { + var code = path.charCodeAt(i) + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1 + break + } + continue + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false + end = i + 1 + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) startDot = i + else if (preDotState !== 1) preDotState = 1 + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1 + } + } + + if ( + startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) + ) { + return '' + } + return path.slice(startDot, end) + }, + + format: function format(pathObject) { + if (pathObject === null || typeof pathObject !== 'object') { + throw new Error('pathObject should be an Object') + } + return _format('/', pathObject) + }, + + parse: function parse(path) { + path = normalizePath(path) + + var ret = { root: '', dir: '', base: '', ext: '', name: '' } + if (path.length === 0) return ret + var code = path.charCodeAt(0) + var isAbsolute = code === CHAR_FORWARD_SLASH + var start + if (isAbsolute) { + ret.root = '/' + start = 1 + } else { + start = 0 + } + var startDot = -1 + var startPart = 0 + var end = -1 + var matchedSlash = true + var i = path.length - 1 + + // Track the state of characters (if any) we see before our first dot and + // after any path separator we find + var preDotState = 0 + + // Get non-dir info + for (; i >= start; --i) { + code = path.charCodeAt(i) + if (code === CHAR_FORWARD_SLASH) { + // If we reached a path separator that was not part of a set of path + // separators at the end of the string, stop now + if (!matchedSlash) { + startPart = i + 1 + break + } + continue + } + if (end === -1) { + // We saw the first non-path separator, mark this as the end of our + // extension + matchedSlash = false + end = i + 1 + } + if (code === CHAR_DOT) { + // If this is our first dot, mark it as the start of our extension + if (startDot === -1) startDot = i + else if (preDotState !== 1) preDotState = 1 + } else if (startDot !== -1) { + // We saw a non-dot and non-path separator before our dot, so we should + // have a good chance at having a non-empty extension + preDotState = -1 + } + } + + if ( + startDot === -1 || + end === -1 || + // We saw a non-dot character immediately before the dot + preDotState === 0 || + // The (right-most) trimmed path component is exactly '..' + (preDotState === 1 && startDot === end - 1 && startDot === startPart + 1) + ) { + if (end !== -1) { + if (startPart === 0 && isAbsolute) + ret.base = ret.name = path.slice(1, end) + else ret.base = ret.name = path.slice(startPart, end) + } + } else { + if (startPart === 0 && isAbsolute) { + ret.name = path.slice(1, startDot) + ret.base = path.slice(1, end) + } else { + ret.name = path.slice(startPart, startDot) + ret.base = path.slice(startPart, end) + } + ret.ext = path.slice(startDot, end) + } + + if (startPart > 0) ret.dir = path.slice(0, startPart - 1) + else if (isAbsolute) ret.dir = '/' + + return ret + }, + + sep: '/', + delimiter: ':', + win32: null, + posix: null, +} + +posix.posix = posix + +module.exports = posix + + /***/ }) /******/ ]); if (key === 'default' && typeof exports === 'function') { diff --git a/package-lock.json b/package-lock.json index 53fda9d..85f7b84 100644 --- a/package-lock.json +++ b/package-lock.json @@ -69,6 +69,11 @@ "resolved": "https://registry.npmjs.org/@skpm/fs/-/fs-0.1.4.tgz", "integrity": "sha512-RDLEH8Ybbecss7pmmur4ib1ruhSm7/SsWtG7MsbZZgorBVJODWttlkCFOAt/+BUu5JeMna7IXrcIUUsVVRDh6Q==" }, + "@skpm/path": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@skpm/path/-/path-0.1.0.tgz", + "integrity": "sha512-ZUJlZ7weEP82MyxMm8bXu4Fm/7RDMr/jQDjPq6qJWNv+UhdbB5Oilgqi3irU3sj7EiuV0RTZ/vFL2TJ0IoyOfQ==" + }, "@skpm/sketch-debugger": { "version": "0.5.1", "resolved": "https://registry.npmjs.org/@skpm/sketch-debugger/-/sketch-debugger-0.5.1.tgz", diff --git a/package.json b/package.json index a8a72d3..00b01f6 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@skpm/child_process": "^0.2.4", "@skpm/dialog": "^0.2.3", "@skpm/fs": "^0.1.4", + "@skpm/path": "^0.1.0", "svgo": "^1.0.5" } } diff --git a/src/plugin.js b/src/plugin.js index db0bb5b..399271c 100644 --- a/src/plugin.js +++ b/src/plugin.js @@ -1,4 +1,5 @@ -import getConfig, {svgoJSONFilePath} from './getConfig' +import getConfig, { svgoJSONFilePath } from './getConfig' +import path from '@skpm/path' import fs from '@skpm/fs' import dialog from '@skpm/dialog' import {spawnSync} from '@skpm/child_process' @@ -7,8 +8,6 @@ import svgoPlugins from './svgo-plugins' import UI from 'sketch/ui' export function openSettings() { - const config = getConfig() - // Plugin was run from the menu, so let's open the settings window const response = dialog.showMessageBox({ buttons: ['Edit SVGO Settings…', 'Reset SVGO Settings', 'Cancel'], @@ -40,12 +39,29 @@ export function compress(context) { if (typeof item.enabled !== 'undefined' && !item.enabled) { return } - const plugin = svgoPlugins[item.name] + let plugin = svgoPlugins[item.name] + if (item.path) { + try { + const loadedPlugin = coscript.require(path.join(String(MSPluginManager.mainPluginsFolderURL().path()), item.path)) + + // loadedPlugin is an NSDictionary so if we try to set something on it, + // it will crash. Instead we move the values to a proper JS object. + var keys = Object.keys(loadedPlugin); + plugin = {}; + Object.keys(loadedPlugin).forEach((k) => { plugin[k] = loadedPlugin[k] }) + if (loadedPlugin.params) { + plugin.params = {} + Object.keys(loadedPlugin.params).forEach((k) => { plugin.params[k] = loadedPlugin.params[k] }) + } + } catch (err) { + log(err) + } + } if (!plugin) { - log('Plugin not found: ' + item.name) + log('Plugin not found: ' + (item.name || item.path)) return } - log('Enabled plugin: ' + item.name) + if (svgoJSON.debug) log('Enabled plugin: ' + (item.name || item.path)) plugin.pluginName = item.name plugin.active = true if (plugin.params) { @@ -55,7 +71,7 @@ export function compress(context) { if (floatPrecision && 'floatPrecision' in plugin.params) { plugin.params.floatPrecision = floatPrecision } - log('—› default params: ' + JSON.stringify(plugin.params, null, 2)) + if (svgoJSON.debug) log('—› default params: ' + JSON.stringify(plugin.params, null, 2)) } if (item.params != null) { if (typeof plugin.params === 'undefined') { @@ -64,7 +80,7 @@ export function compress(context) { for (var attrname in item.params) { plugin.params[attrname] = item.params[attrname] } - log('—› resulting params: ' + JSON.stringify(plugin.params, null, 2)) + if (svgoJSON.debug) log('—› resulting params: ' + JSON.stringify(plugin.params, null, 2)) } parsedSVGOPlugins.push([plugin]) }) @@ -79,7 +95,7 @@ export function compress(context) { } if (filesToCompress.length > 0) { - log('Let‘s go…') + if (svgoJSON.debug) log('Let‘s go…') let originalTotalSize = 0 let compressedTotalSize = 0 if (typeof svgoJSON.full === 'undefined') { svgoJSON.full = true } @@ -105,7 +121,7 @@ export function compress(context) { if (plugin.pluginName == "cleanupIDs") { const parts = currentFile.split('/') var prefix = parts[parts.length - 1].replace('.svg', '').replace(/\s+/g, '-').toLowerCase() + "-" - log('Setting cleanupIDs prefix to: ' + prefix) + if (svgoJSON.debug) log('Setting cleanupIDs prefix to: ' + prefix) plugin.params['prefix'] = prefix } })