Skip to content

Commit

Permalink
Merge pull request #224 from glorious-codes/improve_external_assets_m…
Browse files Browse the repository at this point in the history
…anagement

Add ability to set attributes for external assets html tags
  • Loading branch information
rafaelcamargo committed Sep 4, 2022
2 parents 5ac713d + 9c4f94f commit bb13853
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 22 deletions.
4 changes: 2 additions & 2 deletions .eslintrc
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@
"quotes": ["error", "single"],
"semi": ["error", "always"],
"complexity": ["error", { "max": 3 }],
"max-lines": ["error", { "max": 115 }],
"max-lines": ["error", { "max": 150 }],
"max-statements": ["error", { "max": 5 },
{ "ignoreTopLevelFunctions": true }
]
Expand All @@ -37,7 +37,7 @@
{
"files": [ "src/**/*.test.js" ],
"rules": {
"max-lines": ["error", { "max": 250 }],
"max-lines": ["error", { "max": 275 }],
"max-statements": ["error", { "max": 15 },
{ "ignoreTopLevelFunctions": true }
]
Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@glorious/pitsby",
"version": "1.31.0",
"version": "1.32.0",
"description": "Docs generator for AngularJS, React, Vue and Vanilla components",
"author": "Rafael Camargo <hello@rafaelcamargo.com>",
"repository": {
Expand Down
59 changes: 42 additions & 17 deletions src/cli/services/webapp-html-index-generator.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,8 +9,8 @@ const _public = {};

_public.init = (config = {}) => {
return new Promise((resolve, reject) => {
const linkTags = buildAssetTags(config.styles, buildLinkTag);
const scriptTags = buildAssetTags(config.scripts, buildScriptTag);
const linkTags = buildAssetTags(config.styles, 'stylesheet');
const scriptTags = buildAssetTags(config.scripts, 'script');
const indexHtml = webappHtmlIndexCustomisation.init(buildIndexHtml(
config,
linkTags,
Expand All @@ -26,40 +26,65 @@ _public.init = (config = {}) => {
});
};

function buildAssetTags(paths = [], tagBuilderAction){
function buildAssetTags(items = [], type){
const tags = [];
paths.forEach(path => {
tags.push(tagBuilderAction(path));
items.forEach(item => {
tags.push(buildAssetTag(item, type));
});
return tags;
}

function buildLinkTag(path){
return `<link href="${buildExternalAssetPath(path)}?t=${Date.now()}" rel="stylesheet">`;
function buildAssetTag(item, type){
const attrs = stringifyAssetTagAttrs(buildAssetTagAttrs(item, type));
return shouldUseScriptTag(item, type) ? `<script ${attrs}></script>` : `<link ${attrs}>`;
}

function buildScriptTag(path){
return `<script src="${buildExternalAssetPath(path)}?t=${Date.now()}"></script>`;
function shouldUseScriptTag(item, type){
return type == 'script' && !isPreloadedAsset(item);
}

function buildAssetTagAttrs(item, type){
const attrs = typeof item == 'string' ? { [buildAssetPathAttrName(item, type)]: item } : item;
return shouldAppendStylesheetRelAttr(attrs, type) ? { ...attrs, rel: 'stylesheet' } : attrs;
}

function buildAssetPathAttrName(item, type){
return type == 'script' && !isPreloadedAsset(item) ? 'src' : 'href';
}

function isPreloadedAsset(item){
return ['preload', 'prefetch'].includes(item.rel);
}

function shouldAppendStylesheetRelAttr(attrs, type){
return !attrs.rel && type == 'stylesheet';
}

function stringifyAssetTagAttrs(attrs){
return Object.entries(attrs).map(([key, value]) => {
return value ? `${key}="${formatExternalAssetAttrValue(key, value)}"` : key;
}).join(' ');
}

function formatExternalAssetAttrValue(attrName, attrValue){
return ['href', 'src'].includes(attrName) ? formatExternalAssetPath(attrValue) : attrValue;
}

function handleExternalScriptTags(scriptTags, projects){
const vueConfig = getProjectEngineConfig(projects, 'vue');
const reactConfig = getProjectEngineConfig(projects, 'react');
if(vueConfig)
scriptTags.unshift(cdnService.buildVueScriptTag(vueConfig.version));
if(reactConfig)
scriptTags.unshift(cdnService.buildReactScriptTag(reactConfig.version));
if(vueConfig) scriptTags.unshift(cdnService.buildVueScriptTag(vueConfig.version));
if(reactConfig) scriptTags.unshift(cdnService.buildReactScriptTag(reactConfig.version));
return scriptTags;
}

function getProjectEngineConfig(projects = [], engine){
return projects.find(project => project.engine == engine);
}

function buildExternalAssetPath(path){
return assetsFilepathFilter.isRelativePath(path) ?
`/external/${parseRelativePath(path)}` :
path;
function formatExternalAssetPath(rawPath){
const path = assetsFilepathFilter.isRelativePath(rawPath) ? `/external/${parseRelativePath(rawPath)}` : rawPath;
return `${path}?t=${Date.now()}`;
}

function parseRelativePath(path){
Expand Down
33 changes: 33 additions & 0 deletions src/cli/services/webapp-html-index-generator.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -98,6 +98,39 @@ describe('Webapp HTML Index Generator', () => {
});
});

it('should optionally accept tag attributes for external assets', done => {
const config = buildPitsbyConfigMock({ projects: [{ engine: 'angular' }] });
const styles = [
{ rel: 'preload', href: './style.css', as: 'style' }
];
const scripts = [
{ crossorigin: '', src: './script.js' },
{ crossorigin: 'use-credentials', src: 'https://some.lib.com/from/cdn.min.js' },
{ rel: 'prefetch', href: './prefetch-script.js', as: 'script' },
{ rel: 'preload', href: './preload-script.js', as: 'script' },
{ type: 'module', src: './es6-script.js' }
];
webappHtmlIndexGenerator.init({ ...config, styles, scripts }).then(() => {
const expectedHTMLTags = [
'<link rel="preload" href="/external/style.css?t=123" as="style">',
'<link rel="prefetch" href="/external/prefetch-script.js?t=123" as="script">',
'<link rel="preload" href="/external/preload-script.js?t=123" as="script">',
'<script crossorigin src="/external/script.js?t=123"></script>',
'<script crossorigin="use-credentials" src="https://some.lib.com/from/cdn.min.js?t=123"></script>',
'<script type="module" src="/external/es6-script.js?t=123"></script>'
];
expectedHTMLTags.forEach(htmlTag => {
expect(fileService.write).toHaveBeenCalledWith(
buildHtmlIndexFilename(),
expect.stringContaining(htmlTag),
expect.any(Function),
expect.any(Function)
);
});
done();
});
});

it('should not include any external assets if no assets have been given', done => {
webappHtmlIndexGenerator.init().then(() => {
expect(fileService.write).toHaveBeenCalledWith(
Expand Down

0 comments on commit bb13853

Please sign in to comment.