elm-spa
Version:
single page apps made easy
235 lines (234 loc) • 11.9 kB
JavaScript
;
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);