Skip to content
Permalink
Browse files

feat: parse and morph fonts

  • Loading branch information...
dariocravero committed May 25, 2019
1 parent 829ffa4 commit 130f8f5a886dd1161aa7a0bcd6cfd5d7b3d32c5e
Showing with 293 additions and 292 deletions.
  1. +7 −2 cli.js
  2. +99 −0 fonts.js
  3. +1 −1 get-files.js
  4. +2 −6 morph/react-dom/morph-font.js
  5. +10 −12 morph/react-native/morph-fonts.js
  6. +0 −5 morph/utils.js
  7. +24 −0 parse-views.js
  8. +9 −0 parse/helpers.js
  9. +56 −28 parse/index.js
  10. +23 −0 process-view-custom-files.js
  11. +25 −0 process-view-files.js
  12. +37 −238 watch.js
9 cli.js
@@ -4,9 +4,10 @@ import { promises as fs } from 'fs'
import chalk from 'chalk'
import cleanup from './clean.js'
import minimist from 'minimist'
import watch from './watch.js'
import updateNotifier from 'update-notifier'
import path from 'path'
import pkg from './package.json'
import updateNotifier from 'update-notifier'
import watch from './watch.js'
;(async function() {
let {
_,
@@ -91,6 +92,10 @@ import pkg from './package.json'
console.log(chalk.magenta('X'), ` = View name is invalid`)
console.log('\n\nPress', chalk.blue('ctrl+c'), 'to stop at any time.\n\n')

if (!path.isAbsolute(input)) {
input = path.normalize(path.join(process.cwd(), input))
}

watch({
as,
local,
@@ -0,0 +1,99 @@
import { promises as fs } from 'fs'
import addToMapSet from './add-to-map-set.js'
import morphFontAsReactDom from './morph/react-dom/morph-font.js'
import morphFontAsReactNative from './morph/react-native/morph-fonts.js'
import path from 'path'
import sort from 'bubblesort'

let morphFont = {
'react-dom': morphFontAsReactDom,
'react-native': morphFontAsReactNative,
}

export async function ensureFontsDirectory(src) {
let fontsDirectory = path.join(src, 'Fonts')

try {
await fs.mkdir(fontsDirectory)
} catch (error) {}
}

let getFontId = file => path.basename(file, path.extname(file))

let fontsOrder = ['eot', 'woff2', 'woff', 'ttf', 'svg', 'otf']

let sortFonts = fonts => {
return new Set(
sort(
[...fonts],
(a, b) => fontsOrder.indexOf(b.type) - fontsOrder.indexOf(a.type)
)
)
}

export function processCustomFonts({ customFonts, files }) {
for (let file of files) {
addToMapSet(customFonts, getFontId(file), file)
}
for (let [id, fonts] of customFonts) {
customFonts.set(id, sortFonts(fonts))
}
}

export async function morphAllFonts({ as, customFonts, src, viewsToFiles }) {
let fontsDirectory = path.join(src, 'Fonts')
let fontsInUse = new Set()

console.log('fontsDirectory', fontsDirectory)

let mapCustomFont = file => ({
type: FONT_TYPES[path.extname(file)],
file: file.replace(fontsDirectory, '.'),
})

for (let view of viewsToFiles.values()) {
if (view.custom) continue

view.parsed.fonts.forEach(font => {
fontsInUse.add(font.id)
})
}

for await (let font of fontsInUse) {
let [family, weight, style = 'normal'] = font.split('-')

let customFontSources = []
if (customFonts.has(font)) {
customFontSources = [...customFonts.get(font)].map(mapCustomFont)
console.log(customFontSources)
}

let code = morphFont[as](
{
id: font,
family,
style,
weight,
},
customFontSources
)

fs.writeFile(path.join(src, 'Fonts', `${font}.js`), code)
}
}

// let removeFont = file => {
// let id = getFontId(file)
// instance.customFonts = instance.customFonts.filter(font => font.id !== id)
// }

let FONT_TYPES = {
'.otf': 'opentype',
'.eot': 'eot',
'.svg': 'svg',
'.ttf': 'truetype',
'.woff': 'woff',
'.woff2': 'woff2',
}

// let isFont = f => Object.keys(FONT_TYPES).includes(path.extname(f))
@@ -26,7 +26,7 @@ export let getFilesViewCustom = async src => {
}
return files
}
export let getFilesFont = src =>
export let getFilesFontCustom = src =>
getFiles(src, [
'**/Fonts/*.eot',
'**/Fonts/*.otf',
@@ -1,8 +1,6 @@
import { isGoogleFont } from '../fonts.js'
import { sortFonts } from '../utils.js'
import sort from 'bubblesort'

export default (font, files) => {
export default (font, sources) => {
let body

if (isGoogleFont(font.family)) {
@@ -11,10 +9,8 @@ export default (font, files) => {
'+'
)}:${font.weight}${font.style === 'italic' ? 'i' : ''}');body{}")`
} else {
let sources = sort(files.filter(src => font.id === src.id), sortFonts)

body = `${sources
.map(src => `import ${src.type} from '${src.relativeFile}'`)
.map(src => `import ${src.type} from '${src.file}'`)
.join('\n')}
injectGlobal(\`@font-face {
@@ -1,14 +1,12 @@
import { sortFonts } from '../utils.js'
import sort from 'bubblesort'
export default (font, sources) => {
return `export default {}`
// TODO implement

export default (fonts, files) => {
let body = `export default {\n`
fonts.forEach(font => {
let sources = sort(files.filter(src => font.id === src.id), sortFonts)

body += `${sources
.map(src => `'${font.id}': require('./${src.file}'),\n`)
.join(',')}`
})
return `${body}}`
// let body = `export default {\n`
// fonts.forEach(font => {
// body += `${sources
// .map(src => `'${font.id}': require('./${src.file}'),\n`)
// .join(',')}`
// })
// return `${body}}`
}
@@ -483,11 +483,6 @@ let TRANSFORM_WHITELIST = {
export let canUseNativeDriver = name =>
STYLES_WHITELIST[name] || TRANSFORM_WHITELIST[name] || false

let fontsOrder = ['eot', 'woff2', 'woff', 'ttf', 'svg', 'otf']

export let sortFonts = (a, b) =>
fontsOrder.indexOf(b.type) - fontsOrder.indexOf(a.type)

export let createId = (node, state, addClassName = true) => {
let id = node.is || node.name
// count repeatead ones
@@ -0,0 +1,24 @@
import maybePrintWarnings from './maybe-print-warnings.js'
import parse from './parse/index.js'
import sortSetsInMap from './sort-sets-in-map.js'

export default function parseViews({
customFonts,
verbose,
viewsById,
viewsToFiles,
}) {
for (let view of viewsToFiles.values()) {
if (view.custom) continue

view.parsed = parse({
customFonts,
id: view.id,
source: view.source,
views: viewsById,
})

maybePrintWarnings(view, verbose)
}
sortSetsInMap(viewsById)
}
@@ -1,4 +1,5 @@
import { isRowStyle, isStyle, STYLE } from './prop-is-style.js'
import { fontFamily as googleFontFamilies } from '../morph/fonts.js'
import DidYouMeanMatcher from './did-you-mean.js'
import isNumber from './prop-is-number.js'
import locales from 'i18n-locales'
@@ -80,8 +81,16 @@ export let makeDidYouMeanBlock = views => {
)
return block => dymBlockMatcher.get(block)
}

export let didYouMeanProp = prop => dymPropMatcher.get(prop)

export let makeDidYouMeanFontFamily = customFonts => {
let dymFontFamilyMatcher = new DidYouMeanMatcher(
googleFontFamilies.concat(customFonts)
)
return family => dymFontFamilyMatcher.get(family)
}

let ANIMATION = /(.+)(?:\s)(spring|linear|easeOut|easeInOut|easeIn|ease)(?:\s?(.*)?)/
let BASIC = /^(Capture|CaptureTextArea|Column|Horizontal|Image|List|Svg|SvgCircle|SvgEllipse|SvgDefs|SvgGroup|SvgLinearGradient|SvgRadialGradient|SvgLine|SvgPath|SvgPolygon|SvgPolyline|SvgRect|SvgSymbol|SvgText|SvgUse|SvgStop|Table|Text|View|Vertical)$/i
let BLOCK = /^(\s*)([A-Z][a-zA-Z0-9]*)(\s+([A-Z][a-zA-Z0-9]*))?$/
@@ -23,12 +23,15 @@ import {
isLocalScope,
isSystemScope,
isUserComment,
makeDidYouMeanFontFamily,
} from './helpers.js'
import { isGoogleFont } from '../morph/fonts.js'
import getLoc from './get-loc.js'
import getTags from './get-tags.js'

export default ({
convertSlotToProps = true,
customFonts,
enableLocalScopes = true,
enableSystemScopes = true,
id,
@@ -49,6 +52,9 @@ export default ({
let warnings = []

let didYouMeanBlock = makeDidYouMeanBlock([...views.keys()])
let didYouMeanFontFamily = makeDidYouMeanFontFamily(
[...customFonts.keys()].map(id => id.split('-')[0])
)

let blockIds = []
let getBlockId = node => {
@@ -83,35 +89,57 @@ export default ({
let lookForFonts = block => {
if (block.properties && (isFontable(block.name) || !block.isBasic)) {
let fontFamilyProp = block.properties.find(p => p.name === 'fontFamily')
if (!fontFamilyProp) return

let family = fontFamilyProp.value

let fontWeightProp = block.properties.find(p => p.name === 'fontWeight')
let weight = fontWeightProp ? fontWeightProp.value.toString() : '400'

let fontStyleProp = block.properties.find(p => p.name === 'fontStyle')
let style = fontStyleProp ? fontStyleProp.value.toString() : 'normal'

if (
!fonts.find(
font =>
font.family === family &&
font.weight === weight &&
font.style === style
)
) {
let font = {
id: `${family}-${weight}${style === 'italic' ? '-italic' : ''}`,
isGoogleFont: isGoogleFont(family),
family,
weight,
style,
}

if (fontFamilyProp) {
let fontFamily = fontFamilyProp.value
let fontWeightProp = block.properties.find(p => p.name === 'fontWeight')
let fontStyleProp = block.properties.find(p => p.name === 'fontStyle')
let fontWeight = fontWeightProp
? fontWeightProp.value.toString()
: '400'

let fontStyle = fontStyleProp
? fontStyleProp.value.toString()
: 'normal'

if (
!fonts.find(
font =>
font.family === fontFamily &&
font.weight === fontWeight &&
font.style === fontStyle
)
) {
fonts.push({
id: `${fontFamily}-${fontWeight}${
fontStyle === 'italic' ? '-italic' : ''
}`,
family: fontFamily,
weight: fontWeight,
style: fontStyle,
})
if (font.isGoogleFont || customFonts.has(font.id)) {
fonts.push(font)
} else {
let meant = didYouMeanFontFamily(family)
if (meant && meant !== family) {
warnings.push({
loc: fontFamilyProp.loc,
type: `The font "${family}" is missing. Did you mean "${meant}" instead?\nIf not, download the font files (eg, "${
font.id
}.woff2", "${font.id}.woff", "${
font.id
}.ttf", etc) and add the to the "Fonts" folder.`,
line: lines[fontFamilyProp.loc.start.line - 1],
})
} else {
warnings.push({
loc: fontFamilyProp.loc,
type: `The font "${family}" is missing.\nDownload the font files (eg, "${
font.id
}.woff2", "${font.id}.woff", "${
font.id
}.ttf", etc) and add the to the "Fonts" folder.`,
line: lines[fontFamilyProp.loc.start.line - 1],
})
}
}
}
}
@@ -0,0 +1,23 @@
import addToMapSet from './add-to-map-set.js'
import path from 'path'

export default function processViewCustomFiles({
files,
viewsById,
viewsToFiles,
}) {
for (let file of files) {
let id = path.basename(file, '.js')

addToMapSet(viewsById, id, file)

viewsToFiles.set(file, {
custom: true,
file,
id,
logic: false,
source: null,
version: 0,
})
}
}

0 comments on commit 130f8f5

Please sign in to comment.
You can’t perform that action at this time.