larry babby and threejs for glsl

This commit is contained in:
Sam
2024-06-24 21:24:00 +12:00
parent 87d5dc634d
commit 907ebae4c0
6474 changed files with 1279596 additions and 8 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present Devon Govett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,168 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function _plugin() {
const data = require("@parcel/plugin");
_plugin = function () {
return data;
};
return data;
}
function _posthtmlParser() {
const data = require("posthtml-parser");
_posthtmlParser = function () {
return data;
};
return data;
}
function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _posthtml() {
const data = _interopRequireDefault(require("posthtml"));
_posthtml = function () {
return data;
};
return data;
}
function _posthtmlRender() {
const data = require("posthtml-render");
_posthtmlRender = function () {
return data;
};
return data;
}
function _semver() {
const data = _interopRequireDefault(require("semver"));
_semver = function () {
return data;
};
return data;
}
var _dependencies = _interopRequireDefault(require("./dependencies"));
var _inline = _interopRequireDefault(require("./inline"));
function _diagnostic() {
const data = _interopRequireDefault(require("@parcel/diagnostic"));
_diagnostic = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
var _default = exports.default = new (_plugin().Transformer)({
canReuseAST({
ast
}) {
return ast.type === 'posthtml' && _semver().default.satisfies(ast.version, '^0.4.0');
},
async parse({
asset
}) {
return {
type: 'posthtml',
version: '0.4.1',
program: (0, _posthtmlParser().parser)(await asset.getCode(), {
lowerCaseTags: true,
lowerCaseAttributeNames: true,
sourceLocations: true,
xmlMode: asset.type === 'xhtml'
})
};
},
async transform({
asset,
options
}) {
if (asset.type === 'htm') {
asset.type = 'html';
}
asset.bundleBehavior = 'isolated';
let ast = (0, _nullthrows().default)(await asset.getAST());
let hasModuleScripts;
try {
hasModuleScripts = (0, _dependencies.default)(asset, ast);
} catch (errors) {
if (Array.isArray(errors)) {
throw new (_diagnostic().default)({
diagnostic: errors.map(error => ({
message: error.message,
origin: '@parcel/transformer-html',
codeFrames: [{
filePath: error.filePath,
language: 'html',
codeHighlights: [error.loc]
}]
}))
});
}
throw errors;
}
const {
assets: inlineAssets,
hasModuleScripts: hasInlineModuleScripts
} = (0, _inline.default)(asset, ast);
const result = [asset, ...inlineAssets];
// empty <script></script> is added to make sure HMR is working even if user
// didn't add any.
if (options.hmrOptions && !(hasModuleScripts || hasInlineModuleScripts)) {
const script = {
tag: 'script',
attrs: {
src: asset.addURLDependency('hmr.js', {
priority: 'parallel'
})
},
content: []
};
const found = findFirstMatch(ast, [{
tag: 'body'
}, {
tag: 'html'
}]);
if (found) {
found.content = found.content || [];
found.content.push(script);
} else {
// Insert at the very end.
ast.program.push(script);
}
asset.setAST(ast);
result.push({
type: 'js',
content: '',
uniqueKey: 'hmr.js'
});
}
return result;
},
generate({
ast,
asset
}) {
return {
content: (0, _posthtmlRender().render)(ast.program, {
closingSingleTag: asset.type === 'xhtml' ? 'slash' : undefined
})
};
}
});
function findFirstMatch(ast, expressions) {
let found;
for (const expression of expressions) {
(0, _posthtml().default)().match.call(ast.program, expression, node => {
found = node;
return node;
});
if (found) {
return found;
}
}
}

View File

@@ -0,0 +1,233 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = collectDependencies;
function _posthtml() {
const data = _interopRequireDefault(require("posthtml"));
_posthtml = function () {
return data;
};
return data;
}
function _srcset() {
const data = require("srcset");
_srcset = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
// A list of all attributes that may produce a dependency
// Based on https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
const ATTRS = {
src: ['script', 'img', 'audio', 'video', 'source', 'track', 'iframe', 'embed', 'amp-img'],
// Using href with <script> is described here: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
href: ['link', 'a', 'use', 'script', 'image'],
srcset: ['img', 'source'],
imagesrcset: ['link'],
poster: ['video'],
'xlink:href': ['use', 'image', 'script'],
content: ['meta'],
data: ['object']
};
// A list of metadata that should produce a dependency
// Based on:
// - http://schema.org/
// - http://ogp.me
// - https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/markup
// - https://msdn.microsoft.com/en-us/library/dn255024.aspx
// - https://vk.com/dev/publications
const META = {
property: ['og:image', 'og:image:url', 'og:image:secure_url', 'og:audio', 'og:audio:secure_url', 'og:video', 'og:video:secure_url', 'vk:image'],
name: ['twitter:image', 'msapplication-square150x150logo', 'msapplication-square310x310logo', 'msapplication-square70x70logo', 'msapplication-wide310x150logo', 'msapplication-TileImage', 'msapplication-config'],
itemprop: ['image', 'logo', 'screenshot', 'thumbnailUrl', 'contentUrl', 'downloadUrl']
};
const FEED_TYPES = new Set(['application/rss+xml', 'application/atom+xml']);
// Options to be passed to `addDependency` for certain tags + attributes
const OPTIONS = {
a: {
href: {
needsStableName: true
}
},
iframe: {
src: {
needsStableName: true
}
},
link(attrs) {
if (attrs.rel === 'stylesheet') {
return {
// Keep in the same bundle group as the HTML.
priority: 'parallel'
};
}
}
};
function collectSrcSetDependencies(asset, srcset, opts) {
let parsed = (0, _srcset().parse)(srcset).map(({
url,
...v
}) => ({
url: asset.addURLDependency(url, opts),
...v
}));
return (0, _srcset().stringify)(parsed);
}
function getAttrDepHandler(attr) {
if (attr === 'srcset' || attr === 'imagesrcset') {
return collectSrcSetDependencies;
}
return (asset, src, opts) => asset.addURLDependency(src, opts);
}
function collectDependencies(asset, ast) {
let isDirty = false;
let hasModuleScripts = false;
let seen = new Set();
let errors = [];
(0, _posthtml().default)().walk.call(ast.program, node => {
let {
tag,
attrs
} = node;
if (!attrs || seen.has(node)) {
return node;
}
seen.add(node);
if (tag === 'meta') {
const isMetaDependency = Object.keys(attrs).some(attr => {
let values = META[attr];
return values && values.includes(attrs[attr]) && attrs.content !== '' && !(attrs.name === 'msapplication-config' && attrs.content === 'none');
});
if (isMetaDependency) {
const metaAssetUrl = attrs.content;
if (metaAssetUrl) {
attrs.content = asset.addURLDependency(attrs.content, {
needsStableName: !(attrs.name && attrs.name.includes('msapplication'))
});
isDirty = true;
asset.setAST(ast);
}
}
return node;
}
if (tag === 'link' && (attrs.rel === 'canonical' || attrs.rel === 'manifest' || attrs.rel === 'alternate' && FEED_TYPES.has(attrs.type)) && attrs.href) {
let href = attrs.href;
if (attrs.rel === 'manifest') {
// A hack to allow manifest.json rather than manifest.webmanifest.
// If a custom pipeline is used, it is responsible for running @parcel/transformer-webmanifest.
if (!href.includes(':')) {
href = 'webmanifest:' + href;
}
}
attrs.href = asset.addURLDependency(href, {
needsStableName: true
});
isDirty = true;
asset.setAST(ast);
return node;
}
if (tag === 'script' && attrs.src) {
let sourceType = attrs.type === 'module' ? 'module' : 'script';
let loc = node.location ? {
filePath: asset.filePath,
start: node.location.start,
end: {
line: node.location.end.line,
// PostHTML's location is inclusive
column: node.location.end.column + 1
}
} : undefined;
let outputFormat = 'global';
if (attrs.type === 'module' && asset.env.shouldScopeHoist) {
outputFormat = 'esmodule';
} else {
if (attrs.type === 'module') {
attrs.defer = '';
}
delete attrs.type;
}
// If this is a <script type="module">, and not all of the browser targets support ESM natively,
// add a copy of the script tag with a nomodule attribute.
let copy;
if (outputFormat === 'esmodule' && !asset.env.supports('esmodules', true)) {
let attrs = Object.assign({}, node.attrs);
copy = {
...node,
attrs
};
delete attrs.type;
attrs.nomodule = '';
attrs.defer = '';
attrs.src = asset.addURLDependency(attrs.src, {
// Keep in the same bundle group as the HTML.
priority: 'parallel',
bundleBehavior: sourceType === 'script' || attrs.async != null ? 'isolated' : undefined,
env: {
sourceType,
outputFormat: 'global',
loc
}
});
seen.add(copy);
}
attrs.src = asset.addURLDependency(attrs.src, {
// Keep in the same bundle group as the HTML.
priority: 'parallel',
// If the script is async it can be executed in any order, so it cannot depend
// on any sibling scripts for dependencies. Keep all dependencies together.
// Also, don't share dependencies between classic scripts and nomodule scripts
// because nomodule scripts won't run when modules are supported.
bundleBehavior: sourceType === 'script' || attrs.async != null ? 'isolated' : undefined,
env: {
sourceType,
outputFormat,
loc
}
});
asset.setAST(ast);
if (sourceType === 'module') hasModuleScripts = true;
return copy ? [node, copy] : node;
}
for (let attr in attrs) {
// Check for virtual paths
if (tag === 'a' && attrs[attr].split('#')[0].lastIndexOf('.') < 1) {
continue;
}
// Check for id references
if (attrs[attr][0] === '#') {
continue;
}
let elements = ATTRS[attr];
if (elements && elements.includes(node.tag)) {
// Check for empty string
if (attrs[attr].length === 0) {
errors.push({
message: `'${attr}' should not be empty string`,
filePath: asset.filePath,
loc: node.location
});
}
let depHandler = getAttrDepHandler(attr);
let depOptionsHandler = OPTIONS[node.tag];
let depOptions = typeof depOptionsHandler === 'function' ? depOptionsHandler(attrs, asset.env) : depOptionsHandler && depOptionsHandler[attr];
attrs[attr] = depHandler(asset, attrs[attr], depOptions);
isDirty = true;
}
}
if (isDirty) {
asset.setAST(ast);
}
return node;
});
if (errors.length > 0) {
throw errors;
}
return hasModuleScripts;
}

View File

@@ -0,0 +1,165 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = extractInlineAssets;
function _rust() {
const data = require("@parcel/rust");
_rust = function () {
return data;
};
return data;
}
function _posthtml() {
const data = _interopRequireDefault(require("posthtml"));
_posthtml = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const SCRIPT_TYPES = {
'application/javascript': 'js',
'text/javascript': 'js',
'application/json': false,
'application/ld+json': 'jsonld',
'text/html': false,
module: 'js'
};
function extractInlineAssets(asset, ast) {
let program = ast.program;
let key = 0;
// Extract inline <script> and <style> tags for processing.
let parts = [];
let hasModuleScripts = false;
(0, _posthtml().default)().walk.call(program, node => {
let parcelKey = (0, _rust().hashString)(`${asset.id}:${key++}`);
if (node.tag === 'script' || node.tag === 'style') {
let value = node.content && node.content.join('');
if (value != null) {
var _node$attrs, _node$location;
let type, env;
if (node.tag === 'style') {
if (node.attrs && node.attrs.type != null) {
type = node.attrs.type.split('/')[1];
} else {
type = 'css';
}
} else if (node.attrs && node.attrs.type != null) {
// Skip JSON
if (SCRIPT_TYPES[node.attrs.type] === false) {
return node;
}
if (SCRIPT_TYPES[node.attrs.type]) {
type = SCRIPT_TYPES[node.attrs.type];
} else {
type = node.attrs.type.split('/')[1];
}
let outputFormat = 'global';
let sourceType = 'script';
let attrs = node.attrs;
if (attrs && attrs.type === 'module') {
if (asset.env.shouldScopeHoist && asset.env.supports('esmodules', true)) {
outputFormat = 'esmodule';
} else {
delete attrs.type;
}
sourceType = 'module';
}
let loc = node.location ? {
filePath: asset.filePath,
start: node.location.start,
end: node.location.end
} : undefined;
env = {
sourceType,
outputFormat,
loc
};
} else {
let loc = node.location ? {
filePath: asset.filePath,
start: node.location.start,
end: node.location.end
} : undefined;
type = 'js';
env = {
sourceType: 'script',
loc
};
}
if (!type) {
return node;
}
if (!node.attrs) {
node.attrs = {};
}
// allow a script/style tag to declare its key
if (node.attrs['data-parcel-key']) {
parcelKey = node.attrs['data-parcel-key'];
}
// Inform packager to remove type, since CSS and JS are the defaults.
if ((_node$attrs = node.attrs) !== null && _node$attrs !== void 0 && _node$attrs.type && node.tag === 'style') {
delete node.attrs.type;
}
// insert parcelId to allow us to retrieve node during packaging
node.attrs['data-parcel-key'] = parcelKey;
asset.setAST(ast); // mark dirty
asset.addDependency({
specifier: parcelKey,
specifierType: 'esm'
});
parts.push({
type,
content: value,
uniqueKey: parcelKey,
bundleBehavior: 'inline',
env,
meta: {
type: 'tag',
// $FlowFixMe
node,
startLine: (_node$location = node.location) === null || _node$location === void 0 ? void 0 : _node$location.start.line
}
});
if (env && env.sourceType === 'module') {
hasModuleScripts = true;
}
}
}
// Process inline style attributes.
let attrs = node.attrs;
let style = attrs === null || attrs === void 0 ? void 0 : attrs.style;
if (attrs != null && style != null) {
attrs.style = asset.addDependency({
specifier: parcelKey,
specifierType: 'esm'
});
asset.setAST(ast); // mark dirty
parts.push({
type: 'css',
content: style,
uniqueKey: parcelKey,
bundleBehavior: 'inline',
meta: {
type: 'attr',
// $FlowFixMe
node
}
});
}
return node;
});
return {
assets: parts,
hasModuleScripts
};
}

View File

@@ -0,0 +1,34 @@
{
"name": "@parcel/transformer-html",
"version": "2.12.0",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/parcel.git"
},
"main": "lib/HTMLTransformer.js",
"source": "src/HTMLTransformer.js",
"engines": {
"node": ">= 12.0.0",
"parcel": "^2.12.0"
},
"dependencies": {
"@parcel/diagnostic": "2.12.0",
"@parcel/plugin": "2.12.0",
"@parcel/rust": "2.12.0",
"nullthrows": "^1.1.1",
"posthtml": "^0.16.5",
"posthtml-parser": "^0.10.1",
"posthtml-render": "^3.0.0",
"semver": "^7.5.2",
"srcset": "4"
},
"gitHead": "2059029ee91e5f03a273b0954d3e629d7375f986"
}

View File

@@ -0,0 +1,127 @@
// @flow
import {Transformer} from '@parcel/plugin';
import type {AST} from '@parcel/types';
import {parser as parse} from 'posthtml-parser';
import nullthrows from 'nullthrows';
import type {PostHTMLExpression, PostHTMLNode} from 'posthtml';
import PostHTML from 'posthtml';
import {render} from 'posthtml-render';
import semver from 'semver';
import collectDependencies from './dependencies';
import extractInlineAssets from './inline';
import ThrowableDiagnostic from '@parcel/diagnostic';
export default (new Transformer({
canReuseAST({ast}) {
return ast.type === 'posthtml' && semver.satisfies(ast.version, '^0.4.0');
},
async parse({asset}) {
return {
type: 'posthtml',
version: '0.4.1',
program: parse(await asset.getCode(), {
lowerCaseTags: true,
lowerCaseAttributeNames: true,
sourceLocations: true,
xmlMode: asset.type === 'xhtml',
}),
};
},
async transform({asset, options}) {
if (asset.type === 'htm') {
asset.type = 'html';
}
asset.bundleBehavior = 'isolated';
let ast = nullthrows(await asset.getAST());
let hasModuleScripts;
try {
hasModuleScripts = collectDependencies(asset, ast);
} catch (errors) {
if (Array.isArray(errors)) {
throw new ThrowableDiagnostic({
diagnostic: errors.map(error => ({
message: error.message,
origin: '@parcel/transformer-html',
codeFrames: [
{
filePath: error.filePath,
language: 'html',
codeHighlights: [error.loc],
},
],
})),
});
}
throw errors;
}
const {assets: inlineAssets, hasModuleScripts: hasInlineModuleScripts} =
extractInlineAssets(asset, ast);
const result = [asset, ...inlineAssets];
// empty <script></script> is added to make sure HMR is working even if user
// didn't add any.
if (options.hmrOptions && !(hasModuleScripts || hasInlineModuleScripts)) {
const script = {
tag: 'script',
attrs: {
src: asset.addURLDependency('hmr.js', {
priority: 'parallel',
}),
},
content: [],
};
const found = findFirstMatch(ast, [{tag: 'body'}, {tag: 'html'}]);
if (found) {
found.content = found.content || [];
found.content.push(script);
} else {
// Insert at the very end.
ast.program.push(script);
}
asset.setAST(ast);
result.push({
type: 'js',
content: '',
uniqueKey: 'hmr.js',
});
}
return result;
},
generate({ast, asset}) {
return {
content: render(ast.program, {
closingSingleTag: asset.type === 'xhtml' ? 'slash' : undefined,
}),
};
},
}): Transformer);
function findFirstMatch(
ast: AST,
expressions: PostHTMLExpression[],
): ?PostHTMLNode {
let found;
for (const expression of expressions) {
PostHTML().match.call(ast.program, expression, node => {
found = node;
return node;
});
if (found) {
return found;
}
}
}

View File

@@ -0,0 +1,295 @@
// @flow
import type {AST, MutableAsset, FilePath} from '@parcel/types';
import type {PostHTMLNode} from 'posthtml';
import PostHTML from 'posthtml';
import {parse, stringify} from 'srcset';
// A list of all attributes that may produce a dependency
// Based on https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes
const ATTRS = {
src: [
'script',
'img',
'audio',
'video',
'source',
'track',
'iframe',
'embed',
'amp-img',
],
// Using href with <script> is described here: https://developer.mozilla.org/en-US/docs/Web/SVG/Element/script
href: ['link', 'a', 'use', 'script', 'image'],
srcset: ['img', 'source'],
imagesrcset: ['link'],
poster: ['video'],
'xlink:href': ['use', 'image', 'script'],
content: ['meta'],
data: ['object'],
};
// A list of metadata that should produce a dependency
// Based on:
// - http://schema.org/
// - http://ogp.me
// - https://developer.twitter.com/en/docs/tweets/optimize-with-cards/overview/markup
// - https://msdn.microsoft.com/en-us/library/dn255024.aspx
// - https://vk.com/dev/publications
const META = {
property: [
'og:image',
'og:image:url',
'og:image:secure_url',
'og:audio',
'og:audio:secure_url',
'og:video',
'og:video:secure_url',
'vk:image',
],
name: [
'twitter:image',
'msapplication-square150x150logo',
'msapplication-square310x310logo',
'msapplication-square70x70logo',
'msapplication-wide310x150logo',
'msapplication-TileImage',
'msapplication-config',
],
itemprop: [
'image',
'logo',
'screenshot',
'thumbnailUrl',
'contentUrl',
'downloadUrl',
],
};
const FEED_TYPES = new Set(['application/rss+xml', 'application/atom+xml']);
// Options to be passed to `addDependency` for certain tags + attributes
const OPTIONS = {
a: {
href: {needsStableName: true},
},
iframe: {
src: {needsStableName: true},
},
link(attrs) {
if (attrs.rel === 'stylesheet') {
return {
// Keep in the same bundle group as the HTML.
priority: 'parallel',
};
}
},
};
function collectSrcSetDependencies(asset, srcset, opts) {
let parsed = parse(srcset).map(({url, ...v}) => ({
url: asset.addURLDependency(url, opts),
...v,
}));
return stringify(parsed);
}
function getAttrDepHandler(attr) {
if (attr === 'srcset' || attr === 'imagesrcset') {
return collectSrcSetDependencies;
}
return (asset, src, opts) => asset.addURLDependency(src, opts);
}
export default function collectDependencies(
asset: MutableAsset,
ast: AST,
): boolean {
let isDirty = false;
let hasModuleScripts = false;
let seen = new Set();
let errors: Array<{|
message: string,
filePath: FilePath,
loc: PostHTMLNode['location'],
|}> = [];
PostHTML().walk.call(ast.program, node => {
let {tag, attrs} = node;
if (!attrs || seen.has(node)) {
return node;
}
seen.add(node);
if (tag === 'meta') {
const isMetaDependency = Object.keys(attrs).some(attr => {
let values = META[attr];
return (
values &&
values.includes(attrs[attr]) &&
attrs.content !== '' &&
!(attrs.name === 'msapplication-config' && attrs.content === 'none')
);
});
if (isMetaDependency) {
const metaAssetUrl = attrs.content;
if (metaAssetUrl) {
attrs.content = asset.addURLDependency(attrs.content, {
needsStableName: !(
attrs.name && attrs.name.includes('msapplication')
),
});
isDirty = true;
asset.setAST(ast);
}
}
return node;
}
if (
tag === 'link' &&
(attrs.rel === 'canonical' ||
attrs.rel === 'manifest' ||
(attrs.rel === 'alternate' && FEED_TYPES.has(attrs.type))) &&
attrs.href
) {
let href = attrs.href;
if (attrs.rel === 'manifest') {
// A hack to allow manifest.json rather than manifest.webmanifest.
// If a custom pipeline is used, it is responsible for running @parcel/transformer-webmanifest.
if (!href.includes(':')) {
href = 'webmanifest:' + href;
}
}
attrs.href = asset.addURLDependency(href, {
needsStableName: true,
});
isDirty = true;
asset.setAST(ast);
return node;
}
if (tag === 'script' && attrs.src) {
let sourceType = attrs.type === 'module' ? 'module' : 'script';
let loc = node.location
? {
filePath: asset.filePath,
start: node.location.start,
end: {
line: node.location.end.line,
// PostHTML's location is inclusive
column: node.location.end.column + 1,
},
}
: undefined;
let outputFormat = 'global';
if (attrs.type === 'module' && asset.env.shouldScopeHoist) {
outputFormat = 'esmodule';
} else {
if (attrs.type === 'module') {
attrs.defer = '';
}
delete attrs.type;
}
// If this is a <script type="module">, and not all of the browser targets support ESM natively,
// add a copy of the script tag with a nomodule attribute.
let copy: ?PostHTMLNode;
if (
outputFormat === 'esmodule' &&
!asset.env.supports('esmodules', true)
) {
let attrs = Object.assign({}, node.attrs);
copy = {...node, attrs};
delete attrs.type;
attrs.nomodule = '';
attrs.defer = '';
attrs.src = asset.addURLDependency(attrs.src, {
// Keep in the same bundle group as the HTML.
priority: 'parallel',
bundleBehavior:
sourceType === 'script' || attrs.async != null
? 'isolated'
: undefined,
env: {
sourceType,
outputFormat: 'global',
loc,
},
});
seen.add(copy);
}
attrs.src = asset.addURLDependency(attrs.src, {
// Keep in the same bundle group as the HTML.
priority: 'parallel',
// If the script is async it can be executed in any order, so it cannot depend
// on any sibling scripts for dependencies. Keep all dependencies together.
// Also, don't share dependencies between classic scripts and nomodule scripts
// because nomodule scripts won't run when modules are supported.
bundleBehavior:
sourceType === 'script' || attrs.async != null
? 'isolated'
: undefined,
env: {
sourceType,
outputFormat,
loc,
},
});
asset.setAST(ast);
if (sourceType === 'module') hasModuleScripts = true;
return copy ? [node, copy] : node;
}
for (let attr in attrs) {
// Check for virtual paths
if (tag === 'a' && attrs[attr].split('#')[0].lastIndexOf('.') < 1) {
continue;
}
// Check for id references
if (attrs[attr][0] === '#') {
continue;
}
let elements = ATTRS[attr];
if (elements && elements.includes(node.tag)) {
// Check for empty string
if (attrs[attr].length === 0) {
errors.push({
message: `'${attr}' should not be empty string`,
filePath: asset.filePath,
loc: node.location,
});
}
let depHandler = getAttrDepHandler(attr);
let depOptionsHandler = OPTIONS[node.tag];
let depOptions =
typeof depOptionsHandler === 'function'
? depOptionsHandler(attrs, asset.env)
: depOptionsHandler && depOptionsHandler[attr];
attrs[attr] = depHandler(asset, attrs[attr], depOptions);
isDirty = true;
}
}
if (isDirty) {
asset.setAST(ast);
}
return node;
});
if (errors.length > 0) {
throw errors;
}
return hasModuleScripts;
}

View File

@@ -0,0 +1,179 @@
// @flow strict-local
import type {AST, MutableAsset, TransformerResult} from '@parcel/types';
import {hashString} from '@parcel/rust';
import type {PostHTMLNode} from 'posthtml';
import PostHTML from 'posthtml';
const SCRIPT_TYPES = {
'application/javascript': 'js',
'text/javascript': 'js',
'application/json': false,
'application/ld+json': 'jsonld',
'text/html': false,
module: 'js',
};
interface ExtractInlineAssetsResult {
hasModuleScripts: boolean;
assets: Array<TransformerResult>;
}
export default function extractInlineAssets(
asset: MutableAsset,
ast: AST,
): ExtractInlineAssetsResult {
let program: PostHTMLNode = ast.program;
let key = 0;
// Extract inline <script> and <style> tags for processing.
let parts: Array<TransformerResult> = [];
let hasModuleScripts = false;
PostHTML().walk.call(program, (node: PostHTMLNode) => {
let parcelKey = hashString(`${asset.id}:${key++}`);
if (node.tag === 'script' || node.tag === 'style') {
let value = node.content && node.content.join('');
if (value != null) {
let type, env;
if (node.tag === 'style') {
if (node.attrs && node.attrs.type != null) {
type = node.attrs.type.split('/')[1];
} else {
type = 'css';
}
} else if (node.attrs && node.attrs.type != null) {
// Skip JSON
if (SCRIPT_TYPES[node.attrs.type] === false) {
return node;
}
if (SCRIPT_TYPES[node.attrs.type]) {
type = SCRIPT_TYPES[node.attrs.type];
} else {
type = node.attrs.type.split('/')[1];
}
let outputFormat = 'global';
let sourceType = 'script';
let attrs = node.attrs;
if (attrs && attrs.type === 'module') {
if (
asset.env.shouldScopeHoist &&
asset.env.supports('esmodules', true)
) {
outputFormat = 'esmodule';
} else {
delete attrs.type;
}
sourceType = 'module';
}
let loc = node.location
? {
filePath: asset.filePath,
start: node.location.start,
end: node.location.end,
}
: undefined;
env = {
sourceType,
outputFormat,
loc,
};
} else {
let loc = node.location
? {
filePath: asset.filePath,
start: node.location.start,
end: node.location.end,
}
: undefined;
type = 'js';
env = {
sourceType: 'script',
loc,
};
}
if (!type) {
return node;
}
if (!node.attrs) {
node.attrs = {};
}
// allow a script/style tag to declare its key
if (node.attrs['data-parcel-key']) {
parcelKey = node.attrs['data-parcel-key'];
}
// Inform packager to remove type, since CSS and JS are the defaults.
if (node.attrs?.type && node.tag === 'style') {
delete node.attrs.type;
}
// insert parcelId to allow us to retrieve node during packaging
node.attrs['data-parcel-key'] = parcelKey;
asset.setAST(ast); // mark dirty
asset.addDependency({
specifier: parcelKey,
specifierType: 'esm',
});
parts.push({
type,
content: value,
uniqueKey: parcelKey,
bundleBehavior: 'inline',
env,
meta: {
type: 'tag',
// $FlowFixMe
node,
startLine: node.location?.start.line,
},
});
if (env && env.sourceType === 'module') {
hasModuleScripts = true;
}
}
}
// Process inline style attributes.
let attrs = node.attrs;
let style = attrs?.style;
if (attrs != null && style != null) {
attrs.style = asset.addDependency({
specifier: parcelKey,
specifierType: 'esm',
});
asset.setAST(ast); // mark dirty
parts.push({
type: 'css',
content: style,
uniqueKey: parcelKey,
bundleBehavior: 'inline',
meta: {
type: 'attr',
// $FlowFixMe
node,
},
});
}
return node;
});
return {
assets: parts,
hasModuleScripts,
};
}