"preact build" try to load an (3rd lib) un-exist ts file
See original GitHub issueDo you want to request a feature or report a bug? bug (maybe)
What is the current behaviour?
I using a 3rd parity library, and preact build
fail due to trying to load an un-exist ts file
- @justinribeiro/lite-youtube
@justinribeiro/lite-youtube
’s file structure,package.json
andlite-youtube.js
are listed in belowpackage.json
’smain
andmodule
are indicate tolite-youtube.js
- but
preact-cli
try to load ts file (lite-youtube.ts
)
problem can be solved by --no-prerender
flag, but is it normal ?
If the current behaviour is a bug, please provide the steps to reproduce.
steps to reproduce
preact create Default preact-cli-build-issue
cd preact-cli-build-issue
npm i @justinribeiro/lite-youtube
- import
@justinribeiro/lite-youtube
and write some code (https://github.com/flameddd/preact-cli-build-issue/commit/a1bb98f837dd91ef54ce440a25856b408ce289b4) npm run build
I had create repo to reproduce this
What is the expected behaviour?
npm run build
success
(according @rschristian explanation) correct error message
Please mention other relevant information.
@justinribeiro/lite-youtube
file structure
.
./lite-youtube.d.ts
./LICENSE
./lite-youtube.js.map
./README.md
./package.json
./lite-youtube.js
@justinribeiro/lite-youtube
package.json
Click to expand package.json
{
"_from": "@justinribeiro/lite-youtube",
"_id": "@justinribeiro/lite-youtube@0.9.1",
"_inBundle": false,
"_integrity": "sha512-IgcpHnovzZGxU4Ec+0c7sSLhrJWflvYliQUmdcwBgyVkGw0ZL9Y8IU/m09NPk9EzIk2HAOWUGLywTVpB785egA==",
"_location": "/@justinribeiro/lite-youtube",
"_phantomChildren": {},
"_requested": {
"type": "tag",
"registry": true,
"raw": "@justinribeiro/lite-youtube",
"name": "@justinribeiro/lite-youtube",
"escapedName": "@justinribeiro%2flite-youtube",
"scope": "@justinribeiro",
"rawSpec": "",
"saveSpec": null,
"fetchSpec": "latest"
},
"_requiredBy": [
"#USER",
"/"
],
"_resolved": "https://registry.npmjs.org/@justinribeiro/lite-youtube/-/lite-youtube-0.9.1.tgz",
"_shasum": "c9f83861daad361d58de76b2a5e078de6fe6b751",
"_spec": "@justinribeiro/lite-youtube",
"_where": "/Users/flameddd/program/preact-cli-build-issue",
"author": {
"name": "Justin Ribeiro",
"email": "justin@justinribeiro.com"
},
"bugs": {
"url": "https://github.com/justinribeiro/lite-youtube/issues"
},
"bundleDependencies": false,
"deprecated": false,
"description": "A web component that loads YouTube embed iframes faster. ShadowDom based version of Paul Irish' concept.",
"devDependencies": {
"@typescript-eslint/eslint-plugin": "^2.29.0",
"@typescript-eslint/parser": "^2.29.0",
"eslint": "^6.8.0",
"eslint-config-google": "^0.14.0",
"eslint-config-prettier": "^6.10.0",
"eslint-plugin-html": "^6.0.0",
"eslint-plugin-lit": "^1.2.0",
"prettier": "^2.0.0",
"typescript": "^3.8.0"
},
"files": [
"lite-youtube.d.ts",
"lite-youtube.js",
"lite-youtube.js.map"
],
"homepage": "https://github.com/justinribeiro/lite-youtube#readme",
"keywords": [
"web components",
"youtube"
],
"license": "MIT",
"main": "lite-youtube.js",
"module": "lite-youtube.js",
"name": "@justinribeiro/lite-youtube",
"repository": {
"type": "git",
"url": "git+ssh://git@github.com/justinribeiro/lite-youtube.git"
},
"scripts": {
"build": "tsc --project tsconfig.json",
"lint": "npm run lint:eslint && npm run lint:prettier",
"lint:eslint": "eslint *.ts --ignore-path .gitignore",
"lint:prettier": "prettier --check *.ts --ignore-path .gitignore",
"prepublishOnly": "npm run build"
},
"types": "lite-youtube.d.ts",
"version": "0.9.1"
}
@justinribeiro/lite-youtube
lite-youtube.js
Click to expand lite-youtube.js
/**
*
* The shadowDom / Intersection Observer version of Paul's concept:
* https://github.com/paulirish/lite-youtube-embed
*
* A lightweight YouTube embed. Still should feel the same to the user, just
* MUCH faster to initialize and paint.
*
* Thx to these as the inspiration
* https://storage.googleapis.com/amp-vs-non-amp/youtube-lazy.html
* https://autoplay-youtube-player.glitch.me/
*
* Once built it, I also found these (👍👍):
* https://github.com/ampproject/amphtml/blob/master/extensions/amp-youtube
* https://github.com/Daugilas/lazyYT https://github.com/vb/lazyframe
*/
export class LiteYTEmbed extends HTMLElement {
constructor() {
super();
this.iframeLoaded = false;
this.setupDom();
}
static get observedAttributes() {
return ['videoid'];
}
connectedCallback() {
this.addEventListener('pointerover', LiteYTEmbed.warmConnections, {
once: true,
});
this.addEventListener('click', () => this.addIframe());
}
get videoId() {
return encodeURIComponent(this.getAttribute('videoid') || '');
}
set videoId(id) {
this.setAttribute('videoid', id);
}
get videoTitle() {
return this.getAttribute('videotitle') || 'Video';
}
set videoTitle(title) {
this.setAttribute('videotitle', title);
}
get videoPlay() {
return this.getAttribute('videoPlay') || 'Play';
}
set videoPlay(name) {
this.setAttribute('videoPlay', name);
}
get videoStartAt() {
return Number(this.getAttribute('videoStartAt') || '0');
}
set videoStartAt(time) {
this.setAttribute('videoStartAt', String(time));
}
get autoLoad() {
return this.hasAttribute('autoload');
}
set autoLoad(value) {
if (value) {
this.setAttribute('autoload', '');
}
else {
this.removeAttribute('autoload');
}
}
get params() {
return `start=${this.videoStartAt}&${this.getAttribute('params')}`;
}
/**
* Define our shadowDOM for the component
*/
setupDom() {
const shadowDom = this.attachShadow({ mode: 'open' });
shadowDom.innerHTML = `
<style>
:host {
contain: content;
display: block;
position: relative;
width: 100%;
padding-bottom: calc(100% / (16 / 9));
}
#frame, #fallbackPlaceholder, iframe {
position: absolute;
width: 100%;
height: 100%;
}
#frame {
cursor: pointer;
}
#fallbackPlaceholder {
object-fit: cover;
}
#frame::before {
content: '';
display: block;
position: absolute;
top: 0;
background-image: url(data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAEAAADGCAYAAAAT+OqFAAAAdklEQVQoz42QQQ7AIAgEF/T/D+kbq/RWAlnQyyazA4aoAB4FsBSA/bFjuF1EOL7VbrIrBuusmrt4ZZORfb6ehbWdnRHEIiITaEUKa5EJqUakRSaEYBJSCY2dEstQY7AuxahwXFrvZmWl2rh4JZ07z9dLtesfNj5q0FU3A5ObbwAAAABJRU5ErkJggg==);
background-position: top;
background-repeat: repeat-x;
height: 60px;
padding-bottom: 50px;
width: 100%;
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
z-index: 1;
}
/* play button */
.lty-playbtn {
width: 70px;
height: 46px;
background-color: #212121;
z-index: 1;
opacity: 0.8;
border-radius: 14%; /* TODO: Consider replacing this with YT's actual svg. Eh. */
transition: all 0.2s cubic-bezier(0, 0, 0.2, 1);
border: 0;
}
#frame:hover .lty-playbtn {
background-color: #f00;
opacity: 1;
}
/* play button triangle */
.lty-playbtn:before {
content: '';
border-style: solid;
border-width: 11px 0 11px 19px;
border-color: transparent transparent transparent #fff;
}
.lty-playbtn,
.lty-playbtn:before {
position: absolute;
top: 50%;
left: 50%;
transform: translate3d(-50%, -50%, 0);
}
/* Post-click styles */
.lyt-activated {
cursor: unset;
}
#frame.lyt-activated::before,
.lyt-activated .lty-playbtn {
display: none;
}
</style>
<div id="frame">
<picture>
<source id="webpPlaceholder" type="image/webp">
<source id="jpegPlaceholder" type="image/jpeg">
<img id="fallbackPlaceholder" referrerpolicy="origin">
</picture>
<button class="lty-playbtn"></button>
</div>
`;
this.domRefFrame = this.shadowRoot.querySelector('#frame');
this.domRefImg = {
fallback: this.shadowRoot.querySelector('#fallbackPlaceholder'),
webp: this.shadowRoot.querySelector('#webpPlaceholder'),
jpeg: this.shadowRoot.querySelector('#jpegPlaceholder'),
};
this.domRefPlayButton = this.shadowRoot.querySelector('.lty-playbtn');
}
/**
* Parse our attributes and fire up some placeholders
*/
setupComponent() {
this.initImagePlaceholder();
this.domRefPlayButton.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`);
this.setAttribute('title', `${this.videoPlay}: ${this.videoTitle}`);
if (this.autoLoad) {
this.initIntersectionObserver();
}
}
/**
* Lifecycle method that we use to listen for attribute changes to period
* @param {*} name
* @param {*} oldVal
* @param {*} newVal
*/
attributeChangedCallback(name, oldVal, newVal) {
switch (name) {
case 'videoid': {
if (oldVal !== newVal) {
this.setupComponent();
// if we have a previous iframe, remove it and the activated class
if (this.domRefFrame.classList.contains('lyt-activated')) {
this.domRefFrame.classList.remove('lyt-activated');
this.shadowRoot.querySelector('iframe').remove();
this.iframeLoaded = false;
}
}
break;
}
default:
break;
}
}
/**
* Inject the iframe into the component body
*/
addIframe() {
if (!this.iframeLoaded) {
const iframeHTML = `
<iframe frameborder="0"
allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen
src="https://www.youtube.com/embed/${this.videoId}?autoplay=1&${this.params}"
></iframe>`;
this.domRefFrame.insertAdjacentHTML('beforeend', iframeHTML);
this.domRefFrame.classList.add('lyt-activated');
this.iframeLoaded = true;
}
}
/**
* Setup the placeholder image for the component
*/
initImagePlaceholder() {
// we don't know which image type to preload, so warm the connection
LiteYTEmbed.addPrefetch('preconnect', 'https://i.ytimg.com/');
const posterUrlWebp = `https://i.ytimg.com/vi_webp/${this.videoId}/hqdefault.webp`;
const posterUrlJpeg = `https://i.ytimg.com/vi/${this.videoId}/hqdefault.jpg`;
this.domRefImg.webp.srcset = posterUrlWebp;
this.domRefImg.jpeg.srcset = posterUrlJpeg;
this.domRefImg.fallback.src = posterUrlJpeg;
this.domRefImg.fallback.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`);
this.domRefImg.fallback.setAttribute('alt', `${this.videoPlay}: ${this.videoTitle}`);
}
/**
* Setup the Intersection Observer to load the iframe when scrolled into view
*/
initIntersectionObserver() {
if ('IntersectionObserver' in window &&
'IntersectionObserverEntry' in window) {
const options = {
root: null,
rootMargin: '0px',
threshold: 0,
};
const observer = new IntersectionObserver((entries, observer) => {
entries.forEach(entry => {
if (entry.isIntersecting && !this.iframeLoaded) {
LiteYTEmbed.warmConnections();
this.addIframe();
observer.unobserve(this);
}
});
}, options);
observer.observe(this);
}
}
/**
* Add a <link rel={preload | preconnect} ...> to the head
* @param {*} kind
* @param {*} url
* @param {*} as
*/
static addPrefetch(kind, url, as) {
const linkElem = document.createElement('link');
linkElem.rel = kind;
linkElem.href = url;
if (as) {
linkElem.as = as;
}
linkElem.crossOrigin = 'true';
document.head.append(linkElem);
}
/**
* Begin preconnecting to warm up the iframe load Since the embed's netwok
* requests load within its iframe, preload/prefetch'ing them outside the
* iframe will only cause double-downloads. So, the best we can do is warm up
* a few connections to origins that are in the critical path.
*
* Maybe `<link rel=preload as=document>` would work, but it's unsupported:
* http://crbug.com/593267 But TBH, I don't think it'll happen soon with Site
* Isolation and split caches adding serious complexity.
*/
static warmConnections() {
if (LiteYTEmbed.preconnected)
return;
// Host that YT uses to serve JS needed by player, per amp-youtube
LiteYTEmbed.addPrefetch('preconnect', 'https://s.ytimg.com');
// The iframe document and most of its subresources come right off
// youtube.com
LiteYTEmbed.addPrefetch('preconnect', 'https://www.youtube.com');
// The botguard script is fetched off from google.com
LiteYTEmbed.addPrefetch('preconnect', 'https://www.google.com');
// TODO: Not certain if these ad related domains are in the critical path.
// Could verify with domain-specific throttling.
LiteYTEmbed.addPrefetch('preconnect', 'https://googleads.g.doubleclick.net');
LiteYTEmbed.addPrefetch('preconnect', 'https://static.doubleclick.net');
LiteYTEmbed.preconnected = true;
}
}
LiteYTEmbed.preconnected = false;
// Register custom element
customElements.define('lite-youtube', LiteYTEmbed);
//# sourceMappingURL=lite-youtube.js.map
Please paste the results of preact info
here.
preact info
Environment Info:
System:
OS: macOS 10.15.2
CPU: (4) x64 Intel(R) Core(TM) i5-4260U CPU @ 1.40GHz
Binaries:
Node: 10.15.1 - ~/.nvm/versions/node/v10.15.1/bin/node
npm: 6.14.4 - ~/.nvm/versions/node/v10.15.1/bin/npm
Browsers:
Chrome: 86.0.4240.183
Edge: 81.0.416.58
Safari: 13.0.4
npmGlobalPackages:
preact-cli: 3.0.3
thanks for ALL preact contributors’ hard work
Issue Analytics
- State:
- Created 3 years ago
- Comments:6 (4 by maintainers)
Top Results From Across the Web
TSConfig Reference - Docs on every TSConfig option
Intro to the TSConfig Reference. A TSConfig file in a directory indicates that the directory is the root of a TypeScript or JavaScript...
Read more >TypeScript | Preact: Fast 3kb React alternative with the same ...
Preact ships TypeScript type definitions, which are used by the library ... Rename your .jsx files to .tsx for TypeScript to correctly parse...
Read more >Typescript complains Property does not exist on type 'JSX ...
We're all here having the same lapse in judgement here trying to camelCase a react element. Chances are if you're reading this you're...
Read more >Dependency resolution - Parcel
As Parcel builds your source code, it discovers dependencies, ... Note that these may only be omitted when importing from a JavaScript or...
Read more >Top Related Medium Post
No results found
Top Related StackOverflow Question
No results found
Troubleshoot Live Code
Lightrun enables developers to add logs, metrics and snapshots to live code - no restarts or redeploys required.
Start FreeTop Related Reddit Thread
No results found
Top Related Hackernoon Post
No results found
Top Related Tweet
No results found
Top Related Dev.to Post
No results found
Top Related Hashnode Post
No results found
Top GitHub Comments
Weird error, definitely need to take a look at that. No idea where that’s coming from as the lib does look to be set up right.
However, the fact that something errored out is correct behaviour. The library you’re trying to use has the following line:
Preact-CLI prerenders in Node, and
HTMLElement
is browser-only. This gives you a few options: opt out of prerendering as you have, or wrap the library in a window check (if (window !== undefined) { <use library here> }
).Generally a sourcemap should either include the sources content (the .ts file is inlined into the .map file as a string), or it links to the file (which definitely means that file should be included when publishing).
In general it’s better to inline sources into the sourcemap files, since it’s easier for bundlers to create derivative sourcemaps that way.
Btw - one other option you could consider would be to dynamically import this module. Dynamic imports are not executed during Preact CLI’s prerendering.