Coder Social home page Coder Social logo

nuxt-community / amp-module Goto Github PK

View Code? Open in Web Editor NEW
204.0 8.0 36.0 3.03 MB

AMP Module for Nuxt 2

Home Page: https://codesandbox.io/s/github/nuxt-community/amp-module/

License: MIT License

JavaScript 44.05% Vue 55.81% SCSS 0.14%
nuxt-module amp

amp-module's Introduction

โšก @nuxtjs/amp

npm version npm downloads Circle CI Codecov Dependencies Standard JS

AMP (Accelerated Mobile Pages) Module for Nuxt 2

Looking for a Nuxt 3 alternative? Try out:

DEMO: https://codesandbox.io/s/github/nuxt-community/amp-module/

๐Ÿ“– Release Notes

Docs

Setup

  1. Add the @nuxtjs/amp dependency with yarn or npm to your project
  2. Add @nuxtjs/amp to the modules section of nuxt.config.js
  3. Configure it:
{
  modules: [
    // Simple usage
    '@nuxtjs/amp',

    // With options
    ['@nuxtjs/amp', { /* module options */ }]
  ]
}

Development

  1. Clone this repository
  2. Install dependencies using yarn install or npm install
  3. Start development server using npm run dev

License

MIT License

Copyright (c) Ahad Birang [email protected]

amp-module's People

Contributors

arkhamvm avatar bulatnsbln avatar dependabot[bot] avatar falvhb avatar farnabaz avatar hans00 avatar harlan-zw avatar j1gs4w avatar mfrascati avatar potato4d avatar renovate-bot avatar renovate[bot] avatar simllll avatar thangman22 avatar tohagan avatar voraczech avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

amp-module's Issues

Auto generated css files font problem

Hello,

I try to learn amp how is it work and thank you for this great module!!!

I face below problem when I build a project or run on the local.

"http://localhost:3000/amp:4:3674 The attribute 'href' in tag 'link rel=stylesheet for fonts' is set to the invalid value '/_nuxt/styles.css'."

This is the style.css on the html

<link rel="stylesheet" href="/_nuxt/styles.css">

Is there any idea why can it be?

Thank you

Not working on Nuxt 2.9

Hi, i installed this module on a Nuxt 2.9.2 project, but it seems that the middleware doesn't identify any route as AMP, even if used as global.
Are there any compatibility issues not addressed?

`amplayout` is NOT working

image

Environment

In the example folder, we've set

{
  amp: 'only',
  amplayout: 'custom-amp-layout',
  layout: 'custom'
}

in pages/custom-layout.vue

Situation

  • Enter the path xxx/custom-layout
  • Why the output HTML renders with layout: 'custom', NOT amplayout: 'custom-amp-layout'?

AMP routes should be inaccessible when amp: false

I have an application in which i have { amp: false } by on nuxt-config, and amp enabled only on specific pages.

Reaching an /amp/something route should return a 404 if something has amp disabled, otherwise Google will index the page and mark it as malformed, since it's not using amp components.

Any way to do this?

Layout render multiple times

Hello. Have same problem on amp-hackernews template.
Nuxt layout render multiple times after enter the page. You can check it with console.log in asyncData or nuxt render function. Has anyone already found a way to solve the problem?

Per-route middleware does not evaluate layout property

Hi, i found out an issue on the middleware, about which i could not find any working solution.
When using the middleware on the router everything works correctly, but when put in the route component, the layout gets completely discarded, and the default layout gets mounted everytime i try to render the /amp/ route.
I found out that the layout property gets evaluated before the middleware kicks in, and so the createCustomLayout method gets bound on the component, but never gets rendered.
Do you have any idea on how to improve this?

Assets paths are not rewritted

I want to use and I'm using assets webpacking.

<amp-img
  alt="Case study"
  width="779"
  height="620"
  src="~assets/images/case-study-image-1.png'"
  layout="responsive"
>

But when I compile it src path stays same and not be rewritted to /_nuxt/something...

Any ideas why?

AMP validator with Tailwind

When using Tailwind with nuxtamp the AMP validator throws error about 'style amp-custom' is too long and breaks hot reloading;
is there a way to disable AMP validator?

$isAMP check is not working

Hi,
I have installed this module and while using this $isAMP check-in component section then its not working.
Here is my code :

if (this.$isAMP) {
   console.log("AMP is working")
}else {
   console.log("AMP is Not working")
}

Full Code--

import _clamp from "lodash/clamp"
import _get from "lodash/get"
import Vue from "vue"
export default {
   amp: 'only',
   ampLayout: 'default',
    props: {
        object: {
            type: Object,
            default: () => ({})
        },
        html: String,
        src: String,
        height: [String, Number],
        width: [String, Number],
        aspect: [String, Number],
        videoSrc: [String, Boolean],
        size: {
            type: String,
            default: "full"
        },
        color: {
            type: String,
            default: ""
        },
        respectMax: {
            type: Boolean,
            default: false
        },
        fillSpace: {
            type: Boolean,
            default: false
        },
        fit: {
            type: String,
            default: "cover"
        },
        poster: {
            type: [String, Boolean],
            default: ""
        },
        volume: {
            type: Number,
            default: 0
        },
        loop: {
            type: Boolean,
            default: true
        },
        progressHandler: {
            type: Function,
            required: false,
            default: () => {}
        },
        useTransition: {
            type: Boolean,
            required: false,
            default: false
        }
    },
    data() {
        return {
            loading: true,
            imageWidth: 0,
            imageHeight: 0,
            inView: false,
            observerImgSettings: {
                callback: this.setImgSrc,
                intersection: {
                    rootMargin: "150px 0px 150px 0px",
                    threshold: 0
                }
            },
            observerVideoSettings: {
                callback: this.setVideoSrc,
                intersection: {
                    rootMargin: "150px 150px 150px 150px",
                    threshold: 0
                }
            }
        }
    },
    computed: {
        aspectPadding() {
            // default to defined aspect, or calculate
            const calculatedAspect =
                (this.parsedHeight / this.parsedWidth) * 100
            return this.aspect || calculatedAspect || 56.25
        },
        classes() {
            return [
                "rsp-image-module",
                "responsive-image",
                `fit-${this.fit}`,
                { loading: this.loading },
                { "fill-space": this.fillSpace },
                { "has-video": this.parsedVideoSrc },
                { "in-view": this.inView }
            ]
        },
        imageTag() {
            // TODO: Add other img attributes
             if (this.$isAMP) {
                console.log("AMP is working")
                
            }else {
                console.log("AMP is Not working")
            }
           
            const fallback = `<img data-umesh="Chandra" src="${this.parsedSrc}" alt="${this.parsedAlt}">`
           
            
            if (this.html) return this.html
            let html = fallback

            // Make src and srcset data attributes
            html = html.replace("src=", "data-src=")
            html = html.replace("srcset=", "data-srcset=")
            return html
        },
        innerHtml() {
            return this.imageTag || ""
        },
        isAcf() {
            // check to see if this is an ACF-serialized object
            // search for the existence of keys that ACF objects have but Rest-Easy ones don't
            return (
                this.object.hasOwnProperty("filesize") &&
                this.object.hasOwnProperty("mime_type") &&
                this.object.hasOwnProperty("modified")
            )
        },
        outerStyles() {
            const styles = {}
            // set max dims if needed
            if (this["respect-max"]) {
                styles["max-width"] = `${this.parsedWidth}px`
                styles["max-height"] = `${this.parsedHeight}px`
            }
            // add color bg if needed
            if (
                this.parsedColor &&
                this.parsedColor !== "transparent" &&
                this.loading
            ) {
                // set color or SVG background depending on settings
                if (this.fit == "cover")
                    styles["background-color"] = this.parsedColor
                else styles["background-image"] = `url("${this.svgBG}")`
                // set fit
                styles["background-size"] = this.fit
            }
            return styles
        },
        parsedAlt() {
            return _get(this, "object.alt", "")
        },
        parsedColor() {
            return (
                this.color ||
                _get(this.object, "primary_color") ||
                "transparent"
            )
        },
        parsedFocus() {
            return _get(this, "object.focus", { x: 50, y: 50 })
        },
        parsedHeight() {
            // default to defined height
            if (this.height) return parseInt(this.height)
            return this.imageHeight
        },
        parsedPoster() {
            if (this.poster === null) return ""
            return this.poster && this.poster.length
                ? this.poster
                : this.parsedSrc
        },
        parsedSrc() {
            // return hardcoded source if we have one
            if (this.src) return this.src
            // return ACF source if we have one (only respects fullscreen)
            if (this.isAcf) {
                return _get(this, "object.sizes.fullscreen", "")
            }
            return _get(this.targetSize, `url`)
        },
        parsedVideoSrc() {
            const metaString =
                _get(this.object, "meta.custom_video_url") ||
                _get(this.object, "alt", "")
            if (this.videoSrc || this.videoSrc === null) return this.videoSrc
            else return String(metaString).includes(".mp4") ? metaString : ""
        },
        parsedWidth() {
            // default to defined width
            if (this.width) return parseInt(this.width)
            return this.imageWidth
        },
        sizerStyles() {
            if (!this.fillSpace) {
                return {
                    paddingBottom: `${this.aspectPadding}%`
                }
            }
            return {}
        },
        svgBG() {
            if (!this.parsedColor || this.parsedColor == "transparent")
                return ""
            return `data:image/svg+xml;utf8,
                    <svg xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink'
                        x='0px' y='0px' viewBox='0 0 ${this.imageWidth} ${this.imageHeight}' xml:space='preserve'>
                        <rect fill='${this.parsedColor}' width='${this.imageWidth}' height='${this.imageHeight}' />
                    </svg>`.replace(/\r?\n|\r/g, "")
        },
        targetSize() {
            // should return an object with { height, html, url, width }
            // return ACF sizes
            if (this.isAcf) {
                return {
                    width: _get(this, "object.sizes.fullscreen-width", 0),
                    height: _get(this, "object.sizes.fullscreen-height", 0),
                    url: _get(this, "object.sizes.fullscreen", "")
                }
            }
            // get sizes from image object
            const sizes = _get(this.object, `sizes`, {})
            // get specified size, or first available size
            return (
                _get(sizes, this.size) || sizes[_get(Object.keys(sizes), "[0]")]
            )
        },
        transitionKey() {
            if (this.useTransition) return this.parsedVideoSrc
            else return false
        }
    },
    watch: {
        object() {
            this.setObjectDimensions()
        },
        innerHtml() {
            this.setMediaClass()
            this.setFocalPoint()
        },
        volume() {
            this.setVolume()
        }
    },
    async mounted() {
        // ignore if our src is undefined
        if (!this.parsedSrc) return
        // const img = new Image()
        // img.src = this.parsedSrc
        // // image was already in cache,
        // // set loading var immediately
        // if (img.complete) this.loading = false
        // // set up height/width if we have an object

        this.setObjectDimensions()
        // wait for image to load...

        this.setMediaClass()
        this.setVolume()
        // make sure the wrapped image is rendered
        await Vue.nextTick()
        // set focal point
        this.setFocalPoint()

        // Check if there's a video progress handler Function
        // If there is pass it the current video details every 100ms
        // If there is but there's no video, pass it false once
        const video = this.$refs.video
        if (this.progressHandler) {
            if (video)
                this.progressInterval = setInterval(() => {
                    this.progressHandler({
                        currentTime: video.currentTime,
                        duration: video.duration,
                        percentComplete: video.currentTime / video.duration
                    })
                }, 100)
            else this.progressHandler(false)
        }
    },
    destroyed() {
        if (this.progressInterval) clearInterval(this.progressInterval)
    },
    methods: {
        setImgSrc(isVisible, entry) {
            // Add in view class once
            if (isVisible) {
                this.inView = isVisible
            }

            if (this.$refs.imageWrap && isVisible && this.loading) {
                let lazyImage = this.$refs.imageWrap.querySelector("img")
                if (lazyImage.dataset.srcset) {
                    lazyImage.srcset = lazyImage.dataset.srcset
                }
                if (lazyImage.dataset.src) {
                    lazyImage.src = lazyImage.dataset.src
                }
                this.loading = false
            }
        },
        setVideoSrc(isVisible, entry) {
            // Add in view class once
            if (isVisible) {
                this.inView = isVisible
            }

            if (this.$refs.video && isVisible && this.loading) {
                const lazyVideo = this.$refs.video

                if (lazyVideo.dataset.poster) {
                    lazyVideo.poster = lazyVideo.dataset.poster
                }
                if (lazyVideo.dataset.src) {
                    lazyVideo.src = lazyVideo.dataset.src
                }
                this.loading = false
            }
        },
        setFocalPoint() {
            // find the wrapped image
            if (
                this.$refs.imageWrap &&
                this.$refs.imageWrap.querySelector("*")
            ) {
                const wrapped = this.$refs.imageWrap.querySelector("*")
                // set its position (default: 50% 50%)
                wrapped.style.objectPosition = `${this.parsedFocus.x}% ${this.parsedFocus.y}%`
            }
        },
        setMediaClass() {
            // give the "media" class to whatever we are rendering (img or video)
            if (!this.$el || !this.$el.querySelector) return
            Vue.nextTick(() => {
                const media = this.$el.querySelector(".image-sizer > *")
                if (media) media.classList.add("media")
            })
        },
        setObjectDimensions() {
            if (this.object) {
                this.imageWidth = _get(this.targetSize, `width`)
                this.imageHeight = _get(this.targetSize, `height`)
            }
        },
        async setVolume() {
            await Vue.nextTick()
            const video = this.$el.querySelector("video")
            if (video) {
                video.volume = _clamp(this.volume, 0, 1)
            }
        }
    }
}

Nuxt Generate Trouble

First tried to generate pages on project with Vuetify. Got many errors cauz of inline css in header that Vuetify does.
Like in open issue external css links in head make it invalid Amp page
Though that errors are a problem of the issue.

Tried fresh nuxt tempate:
? Choose programming language JavaScript
? Choose the package manager Yarn
? Choose UI framework None
? Choose custom server framework None (Recommended)
? Choose Nuxt.js modules Axios, Progressive Web App (PWA) Support
? Choose linting tools ESLint, Prettier
? Choose test framework None
? Choose rendering mode Universal (SSR)
? Choose development tools jsconfig.json (Recommended for VS Code)

Added @nuxt/amp with yarn, added '@nuxtjs/amp' to modules in nuxt.config, created default.amp.vue, made changes to index.vue as written in AMP Setup page and AMP Usage Page.

Now have no errors when visited amp page.

Tried yarn generate and got just regular pages. No amp pages was generated.

So...

  1. how to make it work?
  2. how to make it work with Vuetify and remove inline css that vuetify write in head?

Amp routing not working in production build

While in dev mode it correctly works all the time, when I start from the build on the first load it gets the correct amp page, but if I refresh it serves the regular page, despite the url still begins with /amp.

I'm serving 2 different pages, the amp one uses an amp layout (manually set, because of #45), and uses the middleware.

layout: "default.amp",
middleware: "amp",

I can reproduce it by making a fresh build: first time I go to amp url amp is ok, on next refresh on that page or similar renders the normal page. You can check on this production url: the page has amp tags put by the middleware, but it's serving the standard page.

https://unaparolaalgiorno.it/amp/significato/allegoria

Maybe a routing problem?

Support CSS scope management like CSS Modules in each Vue component

Hi, is it possible to support CSS scope management like CSS Modules in Vue components?

Currently AMP supports in Nuxt requires to write CSSs in one place just like old-fashioned way and it feels like killing one of the benefits of Vue.js (which is encapsulation of HTML, CSS, and JS in one place.)

I guess it is necessary to follow the AMP restriction which <style amp-custom> can only live one in a page.

It is great if amp-module somehow handles this restriction and puts scoped CSSs into one <style amp-custom> ๐Ÿ˜„

Any thoughts?

Better SSR support

I'm experiencing a few problems with SSR, which I believe this module doesn't fully support.

  • the whole project CSS (amp and non-amp) is extracted with extractCss:true in amp mode, making the css huge and unsuitable for optimization. I ended up processing html with the route:render hook to replace it with my own
  • when non-amp version is SSR rendered, it includes the amp template, which is totally useless. If the route doesn't contain amp the module shouldn't be loaded at all; could it be a solution? I don't see a case for switching from non amp to amp in the browser.

How to use AMP elements with JSON inline? With DisableSanitizers?

I want to generate a bookend on Amp Story page, with json inline.
But I need a way to not Sanitize the script.
Like nuxt-jsonld does. There's a way?

<amp-story-bookend layout="nodisplay">
    <script type="application/json">
       {
         "bookendVersion": "v1.0",
         "shareProviders": [ ... ],
         "components": [ ... ]
      }
    </script>
  </amp-story-bookend>

The same with AMP Analytics, Animation

<amp-analytics type="gtag" data-credentials="include">
<script type="application/json">
{
  "vars" : {
    "gtag_id": "<GA_MEASUREMENT_ID>",
    "config" : {
      "<GA_MEASUREMENT_ID>": { "groups": "default" }
    }
  }
}
</script>

external css links in head make it invalid Amp page

in "nuxt.cofig.js" File 'extractCss' is true and it works for all layouts & i can't make any exception for my ampLayout

in order to make a valid amp page i need to put all css codes internal (in head tag)

amp-state can't set to string

Hi,

I am trying to use amp-state to store some data returned from asyncData, and use it to change the src in amp-list. However, it seems like that I can't set amp-state as below:

<script type="application/json"> { "foo": "bar" } </script>

The error "The script tag contains invalid JSON that cannot be parsed." keeps showing up in the console. The only exception is that it can be set as a number, or a string of number, it just can't be set as a string of characters.

As a result, I can't use it to generate the amp-list for my dynamic routes, and it has been troubling me for a while now. Is there a way to get around this? Any help would be appreciated.

axios usage breaks AMP on page

Offending code:

const data = await this.$axios.$get('http://localhost:1323/api')

After 2-3 refreshes my AMP Validator extension reports several errors:

The mandatory attribute 'โšก' is missing in tag 'html'.
The mandatory attribute 'amp-custom' is missing in tag 'style amp-custom'.
The mandatory attribute 'amp-custom' is missing in tag 'style amp-custom'.
The mandatory attribute 'amp-custom' is missing in tag 'style amp-custom'.
Custom JavaScript is not allowed.
Custom JavaScript is not allowed.
Custom JavaScript is not allowed.
The mandatory tag 'link rel=canonical' is missing or incorrect.
The mandatory tag 'meta name=viewport' is missing or incorrect.
The mandatory tag 'head > style[amp-boilerplate]' is missing or incorrect.
The mandatory tag 'noscript > style[amp-boilerplate]' is missing or incorrect.
The mandatory tag 'noscript enclosure for boilerplate' is missing or incorrect.
The mandatory tag 'amphtml engine v0.js script' is missing or incorrect.

The interesing thing is that the HTML from the server is valid, the invalidation happens 1-2 seconds after page load.

While this (after replacing the original code) works normal:

const data = await (await fetch('http://localhost:1323/api')).json()

I suggest it has something to do with axios loading bars.

The problem persisted on two separate nuxt installs, nuxt: 2.0.0 and nuxt-edge: 3.0.0.

Errors with amp-story-bookend

Hi,

I'm receiving the following errors that are related to my amp-story-bookend. Any help would be greatly appreciated. The GH is public (https://github.com/daletom/dalesite/blob/master/pages/disney.vue) But I've included the errors and code below. Thanks!

<amp-story-bookend layout="nodisplay"> <script type"application/json" v-html="json" /> </amp-story-bookend>

153:21 error Parsing error: unexpected-character-in-attribute-name vue/no-parsing-error
153:34 error Parsing error: unexpected-solidus-in-tag vue/no-parsing-error
153:38 error Parsing error: unexpected-character-in-attribute-name vue/no-parsing-error
153:40 warning 'v-html' directive can lead to XSS attack vue/no-v-html

Then I get this error when I entered the json data for the bookend

163:5 error Unexpected labeled statement no-labels

<script> export default { amp: true, data() { json: JSON.stringify({ bookendVersion: 'v1.0', shareProviders: ['facebook', 'twitter', 'email'], components: [ { type: 'heading', text: 'More Sections on Zeek' }, { type: 'portrait', title: "Zeek's First Halloween", url: '/halloween/', image: 'https://tom.imgix.net/halloween.jpg?auto=format,compress&w=720&h=1280&fit=fill&fill=blur', category: 'Halloween' }, { type: 'portrait', title: "Zeek's First Birthday", url: '/birthday/', image: 'https://tom.imgix.net/birthday_zeek_birthday.HEIC?auto=format,compress&w=720&h=1280&fit=fill&fill=blur', category: 'Halloween' }, { type: 'cta-link', links: [ { text: 'Go Back Home', url: '/' } ] } ] }) } } </script>

css size is too big than 50kb

Is there any way to extract only the CSS related to the current page for the amp page, without global CSS or the CSS related to the plugin?

how to bind the response data on amp element?

      <section v-for="item in navList" :key="item.navId">
        <h2 >{{item.label}}</h2>

        <amp-accordion disable-session-states>
          <section  v-if="item.subNav" v-for="subItem in item.subNav" :key="subItem.subNavId">
            <h5 :id="'nav'+subItem.subNavId" :on="'tap:navigateTo(url=\''+subItem.url+'\',targe=\'_blank\')'">{{subItem.label}}</h5>
            <p></p>
          </section>
        </amp-accordion>

      </section>

can I bind data like this way?

Release new version

Please release a new version as we currently can't use this module.
We depend onto route.alias and this gets overwritten by the latest release while it is already fixed for several months now.

Update amp-sticky-ads to 1.0 version

I get error at google console when using amp-sticky-ads.
An AMP component 'script' tag is using a deprecated version. Use a more recent version of this AMP component.

This is the current version

<script async custom-element="amp-sticky-ad" src="https://cdn.ampproject.org/v0/amp-sticky-ad-1.0.js"></script>

The tag 'rel=canonical' appears more than once with i18 module

Hi
Error The tag 'link rel=canonical' appears more than once in the document.
if using i18 module + amp.

in ''/ru/amp"

<link data-n-head="ssr" rel="canonical" data-hid="canonical" href="https://host.com/ru/amp">
<link data-n-head="ssr" data-hid="canonical-lang-en" rel="canonical" href="https://host.com/en/amp">

But needed to main page only.

<link rel="canonical" href="https://host.com/ru">

About rules https://amp.dev/documentation/guides-and-tutorials/optimize-and-measure/discovery/?format=websites

config

i18n: {
    seo: true,
    // differentDomains: true,
    baseUrl: process.env.ORIGIN_URL',
    // Routes generation strategy, can be set to one of the following:
    // - 'no_prefix': routes won't be prefixed
    // - 'prefix_except_default': add locale prefix for every locale except default
    // - 'prefix': add locale prefix for every locale
    // - 'prefix_and_default': add locale prefix for every locale and default
    strategy: 'prefix_and_default',
    detectBrowserLanguage: {
      useCookie: true,
      cookieKey: 'i18n_redirected'
    },
    locales: [
      {
        code: 'ru',
        name: 'Russian',
        file: 'ru-RU.js',
        iso: 'ru-RU',
      },
      {
        code: 'en',
        name: 'English',
        file: 'en-US.js',
        iso: 'en-US',
      }
    ],
    lazy: true,
    langDir: 'static/lang/',
    defaultLocale: 'ru'
  },
  amp: {
    origin: process.env.ORIGIN_URL || 'http://localhost:3000',
    mode: 'hybrid' // could use `only` or `false` as well
  },

Bug or features?

Example for Error Page in AMP

Have a example of Error Page in AMP to put in Layouts?
This is not working for me:

<template>
  <div>
    <nuxt-link to="/">Home page</nuxt-link>
  </div>
</template>

<script>
export default {
  amp: 'only'
}
</script>

Origin with empty string is causing canonical meta not be appended on AMP pages

I'm using the amp-module on a project and deploying with Vercel. In this case I know the production url, but I need to also test in preview deployments with dynamically generated base url's. I expect that the origin as empty string ('') should make this work:

...
amp: {
  origin: '',
  mode: 'hybrid'
},
...

Expected result:

  • "canonical" link added on AMP pages
  • "amphtml" link added on non-AMP pages

Actual result:

  • "canonical" link NOT added on AMP pages
  • "amphtml" link added on non-AMP pages

The logic for this is in plugin.js:

if (origin && !head.link.find(l => l.rel === 'canonical' || l.hid === 'canonical')) {

Why is this condition checking if origin is truthy?
The same condition does not apply for appending the amphtml meta.

nuxt generate with dynamic routes

Hi,

I am running a fully static website, with dynamic routes. When I execute nuxt generate, on the dist/amp folder all the dynamic files are missing.

I've tried to manually add the routes into the routeAliases without success.

Is there any way to make it work with dynamic routes?

Thank you

amp elements are not injected

I try AMP Module for nuxt but elements ar not injected correctly

"Unknown custom element: - did you register the component correctly?"

Any idea ? Do i have to declare them in nuxt config ?

Thxs in advance

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.