Skip to content
Permalink
Browse files

feat: morph views inside folders with repeated names

  • Loading branch information...
dariocravero committed May 24, 2019
1 parent ad7b7db commit c5309072888668fe71208c8264ef509de8e6b0d2
Showing with 960 additions and 624 deletions.
  1. +8 −15 index.js
  2. +18 −15 morph/react-dom.js
  3. +0 −1 morph/react-native.js
  4. +50 −0 morph/react/make-get-import.js
  5. +1 −0 package.json
  6. +28 −27 parse/helpers.js
  7. +28 −5 parse/index.js
  8. +6 −0 relativise.js
  9. +816 −561 watch.js
  10. +5 −0 yarn.lock
@@ -4,34 +4,29 @@ import morphFont from './morph/font.js'
import restrictedNames from './restricted-names.js'
import toPascalCase from 'to-pascal-case'
import prettier from 'prettier'
import parse from './parse/index.js'

let DEFAULT_IMPORT = name => `import ${name} from './${name}.view.js'`

export let morph = ({
as,
enableAnimated,
file = {},
getFont,
getImport = DEFAULT_IMPORT,
getSystemImport = () => {},
isStory,
local = 'en',
localSupported = [],
name,
track = true,
views = {},
view,
viewsById,
viewsToFiles,
}) => {
let morphed = doMorph[as]({
enableAnimated,
file,
getFont,
getImport,
getSystemImport,
isStory,
local,
localSupported,
name,
track,
views,
view,
viewsById,
viewsToFiles,
})

morphed.code = prettier.format(morphed.code, {
@@ -58,5 +53,3 @@ export let isViewNameRestricted = (view, as) =>
restrictedNames[as].includes(view)

export { morphFont }

export { parse }
@@ -2,6 +2,7 @@ import * as visitor from './react-dom/block.js'
import getStyleForProperty from './react-dom/get-style-for-property.js'
import getStyles from './react-dom/get-styles.js'
import getValueForProperty from './react-dom/get-value-for-property.js'
import makeGetImport from './react/make-get-import.js'
import maybeUsesRouter from './react-dom/maybe-uses-router.js'
import restrictedNames from './react-dom/restricted-names.js'
import toComponent from './react/to-component.js'
@@ -16,16 +17,17 @@ let imports = {
}

export default ({
file,
getFont = () => false,
getImport,
getSystemImport,
isStory = () => true,
local,
localSupported,
name,
track = true,
views,
view,
viewsById,
viewsToFiles,
}) => {
let name = view.id
let finalName = restrictedNames.includes(name) ? `${name}1` : name
if (name !== finalName) {
console.warn(
@@ -41,7 +43,6 @@ export default ({
cssDynamic: false,
cssStatic: false,
dependencies: new Set(),
file,
flow: null,
flowSetState: false,
getFont,
@@ -90,20 +91,22 @@ export default ({
)
}

let parsed = views[name]
state.fonts = parsed.fonts
state.slots = parsed.slots
state.fonts = view.parsed.fonts
state.slots = view.parsed.slots
state.localSupported = localSupported

walk(parsed.view, visitor, state)
walk(view.parsed.view, visitor, state)
maybeUsesRouter(state)

let finalGetImport = (name, isLazy) =>
imports[name] || getImport(name, isLazy)

return {
code: toComponent({
getImport: finalGetImport,
getImport: makeGetImport({
imports,
getSystemImport,
view,
viewsById,
viewsToFiles,
}),
getStyles,
name: finalName,
state,
@@ -112,7 +115,7 @@ export default ({
flow: state.flow,
flowDefaultState: state.flowDefaultState,
// TODO flow supported states
fonts: parsed.fonts,
slots: parsed.slots,
fonts: view.parsed.fonts,
slots: view.parsed.slots,
}
}
@@ -17,7 +17,6 @@ let imports = {
}

export default ({
file,
getFont = () => false,
getImport,
isStory = () => true,
@@ -0,0 +1,50 @@
import relativise from '../../relativise.js'
import path from 'path'

export default function makeGetImport({
imports,
getSystemImport,
view,
viewsById,
viewsToFiles,
}) {
return function getImport(id, isLazy) {
if (imports[id]) return imports[id]

let externalImport = getSystemImport(id, view.file)
if (externalImport) return externalImport

if (!viewsById.has(id)) {
throw new Error(
`Import "${id}" doesn't exist. It's being imported from ${view.id}.`
)
}

let importCandidates = viewsById.get(id)
let importViewFile = [...importCandidates][0]
if (importCandidates.size > 1) {
let pathToView = view.file.replace(/\.view$/, '')
let maybeFileViewInside = path.join(pathToView, `${id}.view`)
let maybeFileViewCustomInside = path.join(pathToView, `${id}.js`)

if (importCandidates.has(maybeFileViewInside)) {
importViewFile = maybeFileViewInside
} else if (importCandidates.has(maybeFileViewCustomInside)) {
importViewFile = maybeFileViewCustomInside
}
}

let importView = viewsToFiles.get(importViewFile)
let importFile = importView.file
if (importView.logic) {
importFile = importView.logic
} else if (!importView.custom) {
importFile = `${importFile}.js`
}
let importPath = relativise(view.file, importFile)

return isLazy
? `let ${id} = React.lazy(() => import('${importPath}'))`
: `import ${id} from '${importPath}'`
}
}
@@ -46,6 +46,7 @@
"color": "^3.0.0",
"debounce": "^1.2.0",
"fast-glob": "^2.2.2",
"firstline": "^2.0.2",
"flatten": "^1.0.2",
"google-fonts-complete": "^1.1.1",
"has-yarn": "^1.0.0",
@@ -8,32 +8,20 @@ let LOCAL_SCOPES = locales.map(item => item.replace(/-/g, ''))

let dymPropMatcher = new DidYouMeanMatcher([
...STYLE,
'at',
'className',
'cx',
'cy',
'd',
'data',
'defaultValue',
'type',
'fill',
'stroke',
'flow',
'from',
'id',
'key',
'viewBox',
'stroke',
'strokeWidth',
'strokeLinecap',
'strokeMiterlimit',
'at',
'd',
'x',
'cx',
'cy',
'r',
'rx',
'ry',
'x1',
'y1',
'y',
'x2',
'y2',
'strokeLinejoin',
'key',
'maxLength',
'onBlur',
'onChange',
'onClick',
@@ -59,23 +47,36 @@ let dymPropMatcher = new DidYouMeanMatcher([
'onMouseUp',
'onWheel',
'onWhen',
'r',
'ref',
'rx',
'ry',
'step',
'stroke',
'stroke',
'strokeLinecap',
'strokeLinejoin',
'strokeMiterlimit',
'strokeWidth',
'tabIndex',
'text',
'type',
'value',
'viewBox',
'when',
'key',
'maxLength',
'step',
'id',
'className',
'x',
'x1',
'x2',
'y',
'y1',
'y2',
])

export let makeDidYouMeanBlock = views => {
let dymBlockMatcher = new DidYouMeanMatcher(
'Capture|CaptureTextArea|Horizontal|Image|List|Svg|SvgCircle|SvgEllipse|SvgDefs|SvgGroup|SvgLinearGradient|SvgRadialGradient|SvgLine|SvgPath|SvgPolygon|SvgPolyline|SvgRect|SvgSymbol|SvgText|SvgUse|SvgStop|Text|View|Vertical'
.split('|')
.concat(Object.keys(views))
.concat(views)
)
return block => dymBlockMatcher.get(block)
}
@@ -31,10 +31,11 @@ export default ({
convertSlotToProps = true,
enableLocalScopes = true,
enableSystemScopes = true,
id,
skipComments = true,
skipInvalidProps = true,
source,
views = {},
views,
}) => {
// convert crlf to lf
let text = source.replace(/\r\n/g, '\n')
@@ -47,7 +48,7 @@ export default ({
let view = null
let warnings = []

let didYouMeanBlock = makeDidYouMeanBlock(views)
let didYouMeanBlock = makeDidYouMeanBlock([...views.keys()])

let blockIds = []
let getBlockId = node => {
@@ -193,10 +194,28 @@ export default ({
}

let meant = didYouMeanBlock(name)
if (meant && meant !== name) {
if (meant) {
if (meant !== name) {
warnings.push({
loc: block.loc,
type: `"${name}" doesn't exist and won't be morphed. Did you mean "${meant}" instead of "${name}"?`,
line,
})
block.skip = true
}
} else {
warnings.push({
loc: block.loc,
type: `"${name}" doesn't exist and won't be morphed.\nCreate the view or rename the block to point to the right view.`,
line,
})
block.skip = true
}

if (id === name) {
warnings.push({
loc: block.loc,
type: `"${name} doesn't exist. Did you mean "${meant}" instead of "${name}"?`,
type: `Is this a typo? You can't use the view within itself.\nRename the view or use a different block instead of ${name} here. This won't be morphed to avoid loops.`,
line,
})
block.skip = true
@@ -606,7 +625,11 @@ That would mean that SomeView in ${block.name} will be replaced by ${
} else {
propNode.defaultValue = false

if (block.isBasic) {
if (
block.isBasic &&
(propNode.tags.style ||
(block.name === 'Text' && propNode.name === 'text'))
) {
warnings.push({
loc,
type: `Add a default value to "${name}" like: "${name} <${slotName} default value"`,
@@ -0,0 +1,6 @@
import path from 'path'

export default function relativise(from, to) {
let r = path.relative(from, to).replace(/\\/g, '/')
return r.substr(r.startsWith('../..') ? 3 : 1)
}

0 comments on commit c530907

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