mirror of
https://github.com/ryannhg/elm-spa.git
synced 2024-11-22 09:44:55 +03:00
generate route to href function
This commit is contained in:
parent
2f3b68db4c
commit
7befcbba86
1
.gitignore
vendored
1
.gitignore
vendored
@ -1,4 +1,5 @@
|
||||
.DS_Store
|
||||
.elm-spa
|
||||
elm-stuff
|
||||
node_modules
|
||||
dist
|
||||
|
@ -9,6 +9,7 @@ import MsgTemplate from '../templates/msg'
|
||||
import ParamsTemplate from '../templates/params'
|
||||
import * as Process from '../process'
|
||||
import { bold, underline, colors, reset, check, dim } from "../terminal"
|
||||
import { isStaticPage } from "../templates/utils"
|
||||
|
||||
export const build = (env : Environment) => () =>
|
||||
createMissingDefaultFiles()
|
||||
@ -58,7 +59,7 @@ const createMissingDefaultFiles = async () => {
|
||||
const scanForStaticPages = async (entries: PageEntry[]) : Promise<string[][]> => {
|
||||
const contents = await Promise.all(entries.map(e => File.read(e.filepath)))
|
||||
return contents
|
||||
.map((content, i) => content.includes('exposing (page)') ? i : undefined)
|
||||
.map((content, i) => isStaticPage(content) ? i : undefined)
|
||||
.filter(a => typeof a === 'number')
|
||||
.map((i : any) => entries[i].segments)
|
||||
}
|
||||
|
@ -1,10 +1,10 @@
|
||||
import { routeTypeDefinition, indent, routeParserList, paramsImports, Options } from "./utils"
|
||||
import { routeTypeDefinition, indent, routeParserList, paramsImports, Options, routeToHref } from "./utils"
|
||||
|
||||
export default (pages : string[][], _options : Options) : string => `
|
||||
module Gen.Route exposing
|
||||
( Route(..)
|
||||
, fromUrl
|
||||
-- , toUrl
|
||||
, toHref
|
||||
)
|
||||
|
||||
${paramsImports(pages)}
|
||||
@ -25,8 +25,13 @@ routes =
|
||||
${indent(routeParserList(pages), 1)}
|
||||
|
||||
|
||||
-- toUrl : Route -> Url
|
||||
-- toUrl route =
|
||||
-- Debug.todo "Gen.Route.toUrl"
|
||||
toHref : Route -> String
|
||||
toHref route =
|
||||
let
|
||||
joinAsHref : List String -> String
|
||||
joinAsHref segments =
|
||||
"/" ++ String.join "/" segments
|
||||
in
|
||||
${indent(routeToHref(pages), 1)}
|
||||
|
||||
`.trimLeft()
|
@ -12,13 +12,18 @@ const isHomepage = (path: string[]) =>
|
||||
const isNotFoundPage = (path: string[]) =>
|
||||
path.join('') === config.reserved.notFound
|
||||
|
||||
// [ 'Users', 'Name_', 'Settings' ] => [ 'Name_' ]
|
||||
// [ 'Users', 'Name_', 'Settings' ] => [ 'Name' ]
|
||||
const dynamicRouteSegments = (path : string[]) : string[] =>
|
||||
isHomepage(path) || isNotFoundPage(path)
|
||||
? []
|
||||
: path.filter(segment => segment.endsWith('_'))
|
||||
: path.filter(isDynamicSegment)
|
||||
.map(segment => segment.substr(0, segment.length - 1))
|
||||
|
||||
const isDynamicSegment = (segment : string) : boolean =>
|
||||
segment !== config.reserved.homepage
|
||||
&& segment !== config.reserved.notFound
|
||||
&& segment.endsWith('_')
|
||||
|
||||
// "AboutUs" => "aboutUs"
|
||||
const fromPascalToCamelCase = (str : string) : string =>
|
||||
str[0].toLowerCase() + str.substring(1)
|
||||
@ -127,6 +132,29 @@ export const routeTypeDefinition = (paths: string[][]) : string =>
|
||||
export const routeParserList = (paths: string[][]) : string =>
|
||||
multilineList(paths.map(routeParserMap))
|
||||
|
||||
export const routeToHref = (paths: string[][]) : string =>
|
||||
caseExpression(paths, {
|
||||
variable: 'route',
|
||||
condition: (path) =>
|
||||
(dynamicRouteSegments(path).length === 0)
|
||||
? routeVariant(path)
|
||||
: `${routeVariant(path)} params`,
|
||||
result: (path) => `joinAsHref ${routeToHrefSegments(path)}`
|
||||
})
|
||||
|
||||
export const routeToHrefSegments = (path: string[]) : string => {
|
||||
const segments = path.filter(p => p !== config.reserved.homepage)
|
||||
const hrefFragments =
|
||||
segments.map(segment =>
|
||||
isDynamicSegment(segment)
|
||||
? `params.${fromPascalToCamelCase(segment.substring(0, segment.length - 1))}`
|
||||
: `"${fromPascalToSlugCase(segment)}"`
|
||||
)
|
||||
return hrefFragments.length === 0
|
||||
? `[]`
|
||||
: `[ ${hrefFragments.join(', ')} ]`
|
||||
}
|
||||
|
||||
export const paramsImports = (paths: string[][]) : string =>
|
||||
paths.map(path => `import Gen.Params.${path.join('.')}`).join('\n')
|
||||
|
||||
@ -193,7 +221,6 @@ const msgVariant = (path: string[]) : string =>
|
||||
const msg = (path: string[]) : string =>
|
||||
`Pages.${path.join('.')}.Msg`
|
||||
|
||||
|
||||
export const pagesInitBody = (paths: string[][]) : string =>
|
||||
indent(caseExpression(paths, {
|
||||
variable: 'route',
|
||||
@ -240,4 +267,14 @@ const destructuredModel = (path: string[], options : Options) : string =>
|
||||
const pageModelArguments = (path: string[], options : Options) : string =>
|
||||
options.isStatic(path)
|
||||
? `params ()`
|
||||
: `params model`
|
||||
: `params model`
|
||||
|
||||
// Used in place of sophisticated AST parsing
|
||||
const exposes = (keyword: string) => (elmSourceCode: string): boolean =>
|
||||
new RegExp(`module\\s(\\S)+\\sexposing(\\s)+\\([^\\)]*${keyword}[^\\)]*\\)`, 'm').test(elmSourceCode)
|
||||
|
||||
export const exposesModel = exposes('Model')
|
||||
export const exposesMsg = exposes('Msg')
|
||||
|
||||
export const isStaticPage = (sourceCode : string) : boolean =>
|
||||
!exposesModel(sourceCode) || !exposesMsg(sourceCode)
|
@ -143,4 +143,59 @@ type Route
|
||||
]
|
||||
`.trim())
|
||||
})
|
||||
})
|
||||
|
||||
test.each([
|
||||
[ [ config.reserved.homepage ], `[]` ],
|
||||
[ [ "AboutUs" ], `[ "about-us" ]` ],
|
||||
[ [ "AboutUs", "Offices" ], `[ "about-us", "offices" ]` ],
|
||||
[ [ "Posts" ], `[ "posts" ]` ],
|
||||
[ [ "Posts", "Id_" ], `[ "posts", params.id ]` ],
|
||||
[ [ "Users", "Name_", "Settings" ], `[ "users", params.name, "settings" ]` ],
|
||||
[ [ "Users", "Name_", "Posts", "Id_" ], `[ "users", params.name, "posts", params.id ]` ],
|
||||
])(".routeVariant(%p)", (input, output) => {
|
||||
expect(Utils.routeToHrefSegments(input)).toBe(output)
|
||||
})
|
||||
})
|
||||
|
||||
describe.each([['Model'], ['Msg']])
|
||||
('Utils.exposes%s', (name: string) => {
|
||||
const fn = (Utils as any)[`exposes${name}`] as (val: string) => boolean
|
||||
|
||||
test('fails for exposing all', () =>
|
||||
expect(fn(`module Layout exposing (..)`)).toBe(false)
|
||||
)
|
||||
|
||||
test(`fails if missing keyword`, () => {
|
||||
expect(fn(`module Layout exposing (OtherImport)`)).toBe(false)
|
||||
expect(fn(`module Layout exposing
|
||||
( OtherImport
|
||||
)
|
||||
`)).toBe(false)
|
||||
})
|
||||
|
||||
test(`works with single-line exposing "${name}"`, () => {
|
||||
expect(fn(`module Layout exposing (${name})`)).toBe(true)
|
||||
expect(fn(`module Layout exposing (OtherImport, ${name})`)).toBe(true)
|
||||
expect(fn(`module Layout exposing (${name}, OtherImport)`)).toBe(true)
|
||||
})
|
||||
|
||||
test(`works with multi-line exposing "${name}"`, () => {
|
||||
expect(fn(`
|
||||
module Layout exposing
|
||||
( ${name}
|
||||
)
|
||||
`)).toBe(true)
|
||||
expect(fn(`
|
||||
module Layout exposing
|
||||
( OtherImport
|
||||
, ${name}
|
||||
)
|
||||
`)).toBe(true)
|
||||
expect(fn(`
|
||||
module Layout exposing
|
||||
( ${name}
|
||||
, OtherImport
|
||||
)
|
||||
`)).toBe(true)
|
||||
})
|
||||
})
|
Loading…
Reference in New Issue
Block a user