|
| 1 | +#!/usr/bin/env node |
| 2 | +/* eslint-disable arrow-parens */ |
| 3 | +'use strict' |
| 4 | + |
| 5 | +/* global Set */ |
| 6 | + |
| 7 | +const fs = require('fs') |
| 8 | +const path = require('path') |
| 9 | +const extension = path.extname |
| 10 | +const { join } = require('path') |
| 11 | +const sh = require('shelljs') |
| 12 | +// sh.config.fatal = true |
| 13 | +const sed = sh.sed |
| 14 | +const replace = fs.readFileSync('css_prefix_migration.json') |
| 15 | + |
| 16 | +// Blame TC39... https://github.com/benjamingr/RegExp.escape/issues/37 |
| 17 | +RegExp.quote = (string) => string.replace(/[-\\^$*+?.()|[\]{}]/g, '\\$&') |
| 18 | +RegExp.quoteReplacement = (string) => string.replace(/[$]/g, '$$') |
| 19 | + |
| 20 | +const getFiles = (directory, allFiles = []) => { |
| 21 | + const files = fs.readdirSync(directory).map(file => join(directory, file)) |
| 22 | + allFiles.push(...files) |
| 23 | + files.forEach((file) => { |
| 24 | + fs.statSync(file).isDirectory() && getFiles(file, allFiles) |
| 25 | + }) |
| 26 | + return allFiles |
| 27 | +} |
| 28 | + |
| 29 | +const findCSSClasses = (filepath, classes) => { |
| 30 | + const file = fs.readFileSync(filepath, 'utf8') |
| 31 | + let cssClasses |
| 32 | + let elementsToReplace = {} |
| 33 | + |
| 34 | + if (path.extname(filepath) === '.pug') { |
| 35 | + const pugRegex = /(\.([a-zA-Z_-]{1}[\w-_]+))/g |
| 36 | + cssClasses = file.match(pugRegex) |
| 37 | + if (Array.isArray(cssClasses)) { |
| 38 | + cssClasses = cssClasses.map(cssClass => { |
| 39 | + let newValue = classes.find(el => el.old === `${cssClass}`) |
| 40 | + if (newValue) { |
| 41 | + newValue = newValue.new |
| 42 | + } else { |
| 43 | + newValue = cssClass |
| 44 | + } |
| 45 | + elementsToReplace = { |
| 46 | + original: cssClass, |
| 47 | + replacement: newValue |
| 48 | + } |
| 49 | + return elementsToReplace |
| 50 | + }) |
| 51 | + cssClasses = cssClasses.filter(cssClass => cssClass.original !== cssClass.replacement) |
| 52 | + } |
| 53 | + } else { |
| 54 | + const htmlRegex = new RegExp(`(?:class)=['|"]([^'|"]*).`,'g') |
| 55 | + const classTags = file.match(htmlRegex) |
| 56 | + if (Array.isArray(classTags)) { |
| 57 | + cssClasses = classTags.map(classTag => { |
| 58 | + const newClassTag = classTag.replace(htmlRegex, '$1').split(' ').map(cssClass => { |
| 59 | + let newValue = classes.find(el => el.old === `.${cssClass}`) |
| 60 | + if (newValue) { |
| 61 | + newValue = newValue.new.replace('.','') |
| 62 | + } else { |
| 63 | + newValue = cssClass |
| 64 | + } |
| 65 | + return newValue |
| 66 | + }) |
| 67 | + elementsToReplace = { |
| 68 | + original: classTag, |
| 69 | + replacement: classTag.replace(classTag.replace(htmlRegex, '$1'), newClassTag.join(' ')) |
| 70 | + } |
| 71 | + return elementsToReplace |
| 72 | + }) |
| 73 | + cssClasses = cssClasses.filter(cssClass => cssClass.original !== cssClass.replacement) |
| 74 | + } |
| 75 | + } |
| 76 | + |
| 77 | + return Array.isArray(cssClasses) ? [...new Set(cssClasses.sort())] : cssClasses |
| 78 | +} |
| 79 | + |
| 80 | +function getUnique(arr, comp) { |
| 81 | + |
| 82 | + const unique = arr.map(e => e[comp]).map((e, i, final) => final.indexOf(e) === i && i).filter(e => arr[e]).map(e => arr[e]) |
| 83 | + return unique; |
| 84 | +} |
| 85 | + |
| 86 | +function findDataAttributes(filepath, allowedDataAttributes, classes) { |
| 87 | + const file = fs.readFileSync(filepath, 'utf8') |
| 88 | + let dataAttributes |
| 89 | + |
| 90 | + const regex = new RegExp(`(?:${allowedDataAttributes.join('|')})=['|"]([^'|"]*)`,'g') |
| 91 | + |
| 92 | + dataAttributes = file.match(regex) |
| 93 | + if (Array.isArray(dataAttributes)) { |
| 94 | + dataAttributes = dataAttributes.map(element => { |
| 95 | + const value = element.replace(regex, '$1') |
| 96 | + let newValue = classes.find(el => el.old === `.${value}`) |
| 97 | + let object = {} |
| 98 | + if (newValue) { |
| 99 | + newValue = newValue.new.replace('.','') |
| 100 | + object = { |
| 101 | + original: element, |
| 102 | + replacement: element.replace(value, newValue) |
| 103 | + } |
| 104 | + } else { |
| 105 | + object = { |
| 106 | + original: element, |
| 107 | + replacement: element |
| 108 | + } |
| 109 | + } |
| 110 | + return object |
| 111 | + }) |
| 112 | + dataAttributes = getUnique(dataAttributes, 'original') |
| 113 | + } |
| 114 | + return Array.isArray(dataAttributes) ? [...new Set(dataAttributes.sort())] : dataAttributes |
| 115 | +} |
| 116 | + |
| 117 | +function main(args) { |
| 118 | + const directory = args[0] |
| 119 | + const newClasses = JSON.parse(replace).replace |
| 120 | + // const EXCLUDED_DIRS = new Set([ |
| 121 | + // '.git', |
| 122 | + // 'node_modules', |
| 123 | + // 'vendor' |
| 124 | + // ]) |
| 125 | + const INCLUDED_EXTENSIONS = new Set([ |
| 126 | + // This extension whitelist is how we avoid modifying binary files |
| 127 | + '.html', |
| 128 | + '.pug' |
| 129 | + ]) |
| 130 | + const DATA_ATTRIBUTES = [ |
| 131 | + 'data-toggle', |
| 132 | + 'data-dismiss' |
| 133 | + ] |
| 134 | + |
| 135 | + const files = getFiles(directory) |
| 136 | + files.forEach((file) => { |
| 137 | + if (!fs.statSync(file).isDirectory()) { |
| 138 | + const classes = findCSSClasses(file, newClasses) |
| 139 | + console.log(classes) |
| 140 | + if (Array.isArray(classes)) { |
| 141 | + classes.forEach(cl => { |
| 142 | + const original = new RegExp(RegExp.quote(cl.original), 'g') |
| 143 | + const replacement = RegExp.quoteReplacement(cl.replacement) |
| 144 | + sed('-i', original, replacement, file) |
| 145 | + }) |
| 146 | + } |
| 147 | + const dataAttributes = findDataAttributes(file, DATA_ATTRIBUTES, newClasses) |
| 148 | + if (Array.isArray(dataAttributes)) { |
| 149 | + console.log(dataAttributes) |
| 150 | + dataAttributes.forEach(dataAttribute => { |
| 151 | + const original = new RegExp(RegExp.quote(dataAttribute.original), 'g') |
| 152 | + const replacement = RegExp.quoteReplacement(dataAttribute.replacement) |
| 153 | + sed('-i', original, replacement, file) |
| 154 | + }) |
| 155 | + } |
| 156 | + } |
| 157 | + }) |
| 158 | +} |
| 159 | + |
| 160 | +main(process.argv.slice(2)) |
0 commit comments