UNPKG

elm-spa

Version:
235 lines (234 loc) 11.9 kB
"use strict"; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); exports.isStaticView = exports.isStaticPage = exports.isStandardPage = exports.exposesViewFunction = exports.exposesPageFunction = exports.exposesMsg = exports.exposesModel = exports.pagesSubscriptionsBody = exports.pagesViewBody = exports.pagesUpdateCatchAll = exports.pagesUpdateBody = exports.pagesInitBody = exports.pagesBundleDefinition = exports.pagesBundleAnnotation = exports.pagesMsgDefinition = exports.pagesModelDefinition = exports.pagesImports = exports.paramsImports = exports.routeToHrefSegments = exports.routeToHref = exports.routeParserList = exports.routeTypeDefinition = exports.routeTypeVariant = exports.routeParserMap = exports.routeParser = exports.paramsRouteParserMap = exports.routeParameters = exports.indent = exports.customType = exports.multilineRecord = exports.multilineList = exports.routeVariant = exports.urlArgumentToPages = exports.options = void 0; const config_1 = __importDefault(require("../config")); exports.options = (kind) => ({ kind, isStaticView: p => kind(p) === 'view', isStaticPage: p => kind(p) === 'static-page', isStandardPage: p => kind(p) === 'page', }); // [ 'Home_' ] => true const isHomepage = (path) => path.join('') === config_1.default.reserved.homepage; // [ 'NotFound' ] => true const isNotFoundPage = (path) => path.join('') === config_1.default.reserved.notFound; // [ 'Users', 'Name_', 'Settings' ] => [ 'Name' ] const dynamicRouteSegments = (path) => isHomepage(path) || isNotFoundPage(path) ? [] : path.filter(isDynamicSegment) .map(segment => segment.substr(0, segment.length - 1)); const isDynamicSegment = (segment) => segment !== config_1.default.reserved.homepage && segment !== config_1.default.reserved.notFound && segment.endsWith('_'); // "AboutUs" => "aboutUs" const fromPascalToCamelCase = (str) => str[0].toLowerCase() + str.substring(1); // "AboutUs" => "about-us" const fromPascalToSlugCase = (str) => str.split('').map((c, i) => i === 0 || c === c.toLowerCase() ? c : `-${c}`).join('').toLowerCase(); // "about-us" => "AboutUs" const fromSlugToPascalCase = (str) => str.split('-').map(c => c[0].toUpperCase() + c.substring(1)).join(''); // "/about-us" => [ "AboutUs" ] // "Pages.AboutUs" => [ "AboutUs" ] // "Pages/AboutUs.elm" => [ "AboutUs" ] exports.urlArgumentToPages = (url) => { // Cleanup common mistakes if (url.endsWith('.elm')) { url = url.split('.elm').join(''); } if (url.startsWith('Pages')) { url = url.substring('Pages'.length); } return url === '/' ? [config_1.default.reserved.homepage] : url.split('/') .map(str => str.split('.')) .reduce((a, b) => a.concat(b)) .filter(a => a).map(seg => seg.startsWith(':') ? seg.slice(1) + '_' : seg) .map(fromSlugToPascalCase); }; // [ "Settings", "Notifications" ] => "Settings__Notifications" exports.routeVariant = (path) => path.join('__'); // General Elm things exports.multilineList = (items) => items.length === 0 ? `[]` : `[ ${items.join('\n, ')}\n]`; exports.multilineRecord = (sep, items) => items.length === 0 ? `{}` : `{ ${items.map(([k, v]) => `${k} ${sep} ${v}`).join('\n, ')}\n}`; exports.customType = (type, variants) => `type ${type}\n = ${variants.join('\n | ')}`; exports.indent = (lines, n = 1) => lines.split('\n') .map(line => [...Array(n)].map(_ => ` `).join('') + line) .join('\n'); // Used by Gen.Route exports.routeParameters = (path) => { const dynamics = dynamicRouteSegments(path); if (dynamics.length === 0) { return `()`; } else { return `{ ${dynamics.map(d => `${fromPascalToCamelCase(d)} : String`).join(', ')} }`; } }; const routeParameters2 = (path) => { const dynamics = dynamicRouteSegments(path); if (dynamics.length === 0) { return ``; } else { return ` { ${dynamics.map(d => `${fromPascalToCamelCase(d)} : String`).join(', ')} }`; } }; const routeParamVariable = (path) => (dynamicRouteSegments(path).length === 0) ? `` : ` params`; const routeParamValue = (path) => (dynamicRouteSegments(path).length === 0) ? `()` : `params`; exports.paramsRouteParserMap = (path) => dynamicRouteSegments(path).length === 0 ? `` : `Parser.map Params `; exports.routeParser = (path) => { const fromPiece = (p) => (p === config_1.default.reserved.homepage) ? `Parser.top` : p.endsWith('_') ? `Parser.string` : `Parser.s "${fromPascalToSlugCase(p)}"`; return `${exports.paramsRouteParserMap(path)}(` + path.map(fromPiece).join(' </> ') + `)`; }; exports.routeParserMap = (path) => `Parser.map ${exports.routeVariant(path)} ${paramsModule(path)}.parser`; exports.routeTypeVariant = (path) => `${exports.routeVariant(path)}${routeParameters2(path)}`; exports.routeTypeDefinition = (paths) => exports.customType(`Route`, paths.map(exports.routeTypeVariant)); exports.routeParserList = (paths) => exports.multilineList(paths.map(exports.routeParserMap)); exports.routeToHref = (paths) => caseExpression(paths, { variable: 'route', condition: (path) => (dynamicRouteSegments(path).length === 0) ? exports.routeVariant(path) : `${exports.routeVariant(path)} params`, result: (path) => `joinAsHref ${exports.routeToHrefSegments(path)}` }); exports.routeToHrefSegments = (path) => { const segments = path.filter(p => p !== config_1.default.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(', ')} ]`; }; exports.paramsImports = (paths) => paths.map(path => `import Gen.Params.${path.join('.')}`).join('\n'); exports.pagesImports = (paths) => paths.map(path => `import ${pageModuleName(path)}`).join('\n'); const pageModuleName = (path) => `Pages.${path.join('.')}`; exports.pagesModelDefinition = (paths, options) => exports.customType('Model', paths.map(path => (() => { if (path[0] === config_1.default.reserved.redirecting) return config_1.default.reserved.redirecting; switch (options.kind(path)) { case 'view': return `${modelVariant(path)} ${params(path)}`; case 'static-page': return `${modelVariant(path)} ${params(path)} ${model(path, options)}`; case 'page': return `${modelVariant(path)} ${params(path)} ${model(path, options)}`; } })())); exports.pagesMsgDefinition = (paths, options) => (paths.length === 0) ? `type Msg = None` : exports.customType('Msg', paths.map(path => `${msgVariant(path)} ${msg(path, options)}`)); exports.pagesBundleAnnotation = (paths, options) => exports.indent(exports.multilineRecord(':', paths.map(path => [ bundleName(path), (() => { switch (options.kind(path)) { case 'view': return `Static ${params(path)}`; case 'static-page': return `Bundle ${params(path)} ${model(path, options)} ${msg(path, options)}`; case `page`: return `Bundle ${params(path)} ${model(path, options)} ${msg(path, options)}`; } })() ]))); exports.pagesBundleDefinition = (paths, options) => exports.indent(exports.multilineRecord('=', paths.map(path => [ bundleName(path), (() => { switch (options.kind(path)) { case 'view': return `static ${pageModuleName(path)}.view Model.${modelVariant(path)}`; case 'static-page': return `bundle ${pageModuleName(path)}.page Model.${modelVariant(path)} Msg.${msgVariant(path)}`; case `page`: return `bundle ${pageModuleName(path)}.page Model.${modelVariant(path)} Msg.${msgVariant(path)}`; } })() ]))); const bundleName = (path) => path.map(fromPascalToCamelCase).join('__'); const paramsModule = (path) => `Gen.Params.${path.join('.')}`; const params = (path) => `${paramsModule(path)}.Params`; const model = (path, options) => { switch (options.kind(path)) { case 'view': return `()`; case 'static-page': return `()`; case 'page': return `Pages.${path.join('.')}.Model`; } }; const modelVariant = (path) => `${path.join('__')}`; const msgVariant = (path) => `${path.join('__')}`; const msg = (path, options) => options.isStandardPage(path) ? `Pages.${path.join('.')}.Msg` : `Never`; exports.pagesInitBody = (paths) => exports.indent(caseExpression(paths, { variable: 'route', condition: path => `Route.${exports.routeVariant(path)}${routeParamVariable(path)}`, result: path => `pages.${bundleName(path)}.init ${routeParamValue(path)}` })); exports.pagesUpdateBody = (paths, options) => exports.indent(caseExpression(paths, { variable: '( msg_, model_ )', condition: path => `( Msg.${msgVariant(path)} msg, ${destructuredModel(path, options)} )`, result: path => `pages.${bundleName(path)}.update params msg model` })); exports.pagesUpdateCatchAll = ` _ -> \\_ _ _ -> ( model_, Effect.none )`; exports.pagesViewBody = (paths, options) => exports.indent(caseExpressionWithRedirectingModel(`\\_ _ _ -> View.none`, paths, { variable: 'model_', condition: path => `${destructuredModel(path, options)}`, result: path => `pages.${bundleName(path)}.view ${pageModelArguments(path, options)}` })); exports.pagesSubscriptionsBody = (paths, options) => exports.indent(caseExpressionWithRedirectingModel(`\\_ _ _ -> Sub.none`, paths, { variable: 'model_', condition: path => `${destructuredModel(path, options)}`, result: path => `pages.${bundleName(path)}.subscriptions ${pageModelArguments(path, options)}` })); const caseExpressionWithRedirectingModel = (fallback, items, options) => caseExpression([[config_1.default.reserved.redirecting]].concat(items), { variable: options.variable, condition: (item) => item[0] === config_1.default.reserved.redirecting ? `Model.${config_1.default.reserved.redirecting}` : options.condition(item), result: (item) => item[0] === config_1.default.reserved.redirecting ? fallback : options.result(item) }); const caseExpression = (items, options) => `case ${options.variable} of ${items.map(item => ` ${options.condition(item)} ->\n ${options.result(item)}`).join('\n\n')}`; const destructuredModel = (path, options) => { switch (options.kind(path)) { case 'view': return `Model.${modelVariant(path)} params`; case 'static-page': return `Model.${modelVariant(path)} params model`; case 'page': return `Model.${modelVariant(path)} params model`; } }; const pageModelArguments = (path, options) => { switch (options.kind(path)) { case 'view': return `params ()`; case 'static-page': return `params model`; case 'page': return `params model`; } }; const exposes = (value) => (str) => { const regex = new RegExp('^module\\s+[^\\s]+\\s+exposing\\s+\\(((?:\\.\\)|[^)])+)\\)'); const match = (str.match(regex) || [])[1]; if (match) { return match.split(',').filter(a => a).map(a => a.trim()).includes(value); } else { return false; } }; exports.exposesModel = exposes('Model'); exports.exposesMsg = (str) => exposes('Msg')(str) || exposes('Msg(..)')(str); exports.exposesPageFunction = exposes('page'); exports.exposesViewFunction = exposes('view'); exports.isStandardPage = (src) => exports.exposesPageFunction(src) && exports.exposesModel(src) && exports.exposesMsg(src); exports.isStaticPage = (src) => exports.exposesPageFunction(src); exports.isStaticView = (src) => exports.exposesViewFunction(src);