123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291 |
- const { createMacro, MacroError } = require('babel-plugin-macros')
- const { addNamed } = require('@babel/helper-module-imports')
- module.exports = createMacro(importer, {
- configName: 'fontawesome-svg-core'
- })
- const styles = [
- 'solid',
- 'regular',
- 'light',
- 'thin',
- 'duotone',
- 'brands'
- ]
- const macroNames = [
- ...styles,
- 'icon'
- ]
- const families = [
- 'classic',
- 'duotone',
- 'sharp'
- ]
- function importer ({references, state, babel, source, config}) {
- const license = (config !== undefined ? config.license : 'free')
- if (!['free', 'pro'].includes(license)) {
- throw new Error(
- "config license must be either 'free' or 'pro'"
- )
- }
- Object.keys(references).forEach((key) => {
- replace({
- macroName: key,
- license,
- references: references[key],
- state,
- babel,
- source
- })
- })
- }
- function replace ({ macroName, license, references, state, babel, source }) {
- references.forEach((nodePath) => {
- const {iconName, style, family} = resolveReplacement({ nodePath, babel, state, macroName })
- const name = `fa${capitalize(camelCase(iconName))}`
- const importFrom = getImport({family, style, license, name})
- const importName = addNamed(nodePath, name, importFrom)
- nodePath.parentPath.replaceWith(importName)
- })
- }
- function getImport ({family, style, license, name}) {
- if (family) {
- return `@fortawesome/${family.toLowerCase()}-${style}-svg-icons/${name}`
- } else if (style === 'brands') {
- return `@fortawesome/free-brands-svg-icons/${name}`
- } else {
- return `@fortawesome/${license}-${style}-svg-icons/${name}`
- }
- }
- function resolveReplacement ({ nodePath, babel, state, macroName }) {
- if('icon' === macroName) {
- return resolveReplacementIcon({ nodePath, babel, state, macroName })
- } else {
- return resolveReplacementLegacyStyle({ nodePath, babel, state, macroName })
- }
- }
- // The macros corresonding to legacy style names: solid(), regular(), light(), thin(), duotone(), brands().
- function resolveReplacementLegacyStyle({ nodePath, babel, state, macroName }) {
- const { types: t } = babel
- const { parentPath } = nodePath
- if (!styles.includes(macroName)) {
- throw parentPath.buildCodeFrameError(
- `${macroName} is not a valid macro name. Use one of ${macroNames.join(', ')}`,
- MacroError
- )
- }
- if (parentPath.node.arguments) {
- if (parentPath.node.arguments.length < 1) {
- throw parentPath.buildCodeFrameError(
- `Received an invalid number of arguments for ${macroName} macro: must be exactly 1`,
- MacroError
- )
- }
- if (parentPath.node.arguments.length > 1) {
- throw parentPath.buildCodeFrameError(
- `Received an invalid number of arguments for ${macroName} macro: must be exactly 1`,
- MacroError
- )
- }
- if (
- (parentPath.node.arguments.length === 1 ||
- parentPath.node.arguments.length === 2) &&
- t.isStringLiteral(parentPath.node.arguments[0]) &&
- nodePath.parentPath.node.arguments[0].value.startsWith('fa-')
- ) {
- throw parentPath.buildCodeFrameError(
- `Don't begin the icon name with fa-, just use ${nodePath.parentPath.node.arguments[0].value.slice(3)}`,
- MacroError
- )
- }
- if ((parentPath.node.arguments.length === 1 ||
- parentPath.node.arguments.length === 2) &&
- !t.isStringLiteral(parentPath.node.arguments[0])) {
- throw parentPath.buildCodeFrameError(
- 'Only string literals are supported when referencing icons (use a string here instead)',
- MacroError
- )
- }
- } else {
- throw parentPath.buildCodeFrameError(
- 'Pass the icon name you would like to import as an argument.',
- MacroError
- )
- }
- return {
- iconName: nodePath.parentPath.node.arguments[0].value,
- style: macroName,
- family: undefined
- }
- }
- // The icon() macro.
- function resolveReplacementIcon ({ nodePath, babel, state, macroName }) {
- const { types: t } = babel
- const { parentPath } = nodePath
- if ('icon' !== macroName) {
- throw parentPath.buildCodeFrameError(
- `${macroName} is not a valid macro name. Use one of ${macroNames.join(', ')}`,
- MacroError
- )
- }
- if (parentPath.node.arguments.length !== 1) {
- throw parentPath.buildCodeFrameError(
- `Received an invalid number of arguments for ${macroName} macro: must be exactly 1`,
- MacroError
- )
- }
- if (!t.isObjectExpression(parentPath.node.arguments[0])) {
- throw parentPath.buildCodeFrameError(
- 'Only object expressions are supported when referencing icons with this macro, like this: { name: \'star\' }',
- MacroError
- )
- }
- const properties = (parentPath.node.arguments[0].properties || [])
- const namePropIndex = properties.findIndex((prop) => 'name' === prop.key.name)
- const name = namePropIndex >= 0
- ? getStringLiteralPropertyValue(t, parentPath, parentPath.node.arguments[0].properties[namePropIndex])
- : undefined
- if(!name) {
- throw parentPath.buildCodeFrameError(
- 'The object argument to the icon() macro must have a name property',
- MacroError
- )
- }
- const stylePropIndex = properties.findIndex((prop) => 'style' === prop.key.name)
- let style = stylePropIndex >= 0
- ? getStringLiteralPropertyValue(t, parentPath, parentPath.node.arguments[0].properties[stylePropIndex])
- : undefined
- if(style && !styles.includes(style)) {
- throw parentPath.buildCodeFrameError(
- `Invalid style name: ${style}. It must be one of the following: ${styles.join(', ')}`,
- MacroError
- )
- }
- const familyPropIndex = properties.findIndex((prop) => 'family' === prop.key.name)
- let family = familyPropIndex >= 0
- ? getStringLiteralPropertyValue(t, parentPath, parentPath.node.arguments[0].properties[familyPropIndex])
- : undefined
- if(family && !families.includes(family)) {
- throw parentPath.buildCodeFrameError(
- `Invalid family name: ${family}. It must be one of the following: ${families.join(', ')}`,
- MacroError
- )
- }
- if('duotone' === style && family && 'classic' !== family) {
- throw parentPath.buildCodeFrameError(
- `duotone cannot be used as a style name with any family other than classic`,
- MacroError
- )
- }
- if('brands' === style && family && 'classic' !== family) {
- throw parentPath.buildCodeFrameError(
- `brands cannot be used as a style name with any family other than classic`,
- MacroError
- )
- }
- if(family && !style) {
- throw parentPath.buildCodeFrameError(
- `When a family is specified, a style must also be specified`,
- MacroError
- )
- }
- if('duotone' === style || 'duotone' === family) {
- family = undefined
- style = 'duotone'
- }
- if('brands' === style) {
- family = undefined
- }
- // defaults
- if(!style) {
- style = 'solid'
- }
- if('classic' === family) {
- family = undefined
- }
- return {
- iconName: name,
- family,
- style
- }
- }
- function getStringLiteralPropertyValue(t, parentPath, property) {
- if(!('object' === typeof t && 'function' === typeof t.isStringLiteral)) {
- throw Error("ERROR: invalid babel-types arg. This is probably a programming error in import.macro")
- }
- if(!('object' === typeof property && 'object' === typeof property.value && 'object' == typeof property.key)) {
- throw Error("ERROR: invalid babel property arg. This is probably a programming error in import.macro")
- }
- if(!('object' === typeof parentPath && 'function' === typeof parentPath.buildCodeFrameError)) {
- throw Error("ERROR: invalid babel parentPath arg. This is probably a programming error in import.macro")
- }
- if(!t.isStringLiteral(property.value)) {
- throw parentPath.buildCodeFrameError(
- `Only string literals are supported for the ${property.key.name} property (use a string here instead)`,
- MacroError
- )
- }
- return property.value.value
- }
- function capitalize (str) {
- return str[0].toUpperCase() + str.slice(1)
- }
- function camelCase (str) {
- return str
- .split('-')
- .map((s, index) => {
- return (
- (index === 0 ? s[0].toLowerCase() : s[0].toUpperCase()) +
- s.slice(1).toLowerCase()
- )
- })
- .join('')
- }
|