How to show favorite project using pivot in vue and laravel - laravel

This is the all projects, and favorite button to show the favorite project only:
This is how I show all project:
<project v-for="(project, index) in projects" :key="project.id" :index="index" :details="project"></project>
This is the button favorite method:
toggleFavorite () {
this.favoriteShown = !this.favoriteShown
this.allProjectShown = !this.allProjectShown
},
I import the project from the other vue file
import project from './project'
And this is the whole favorite method in project.vue
And maybe you need to see the project.vue export default:
So, what should I add in this code to show the favorite projects only
<project v-for="(project, index) in projects" :key="project.id" :index="index" :details="project"></project>

Define a computed property that filters your projects returning just the objects that have a true value for the favorite property.
I'm making some assumptions regarding the structure of your project object, however, the principle will remain the same.
An example of how you could filter results below:
let projects = [
{
id: 1,
favorite: true
},
{
id: 2,
favorite: false
},
{
id: 3,
favorite: false
},
{
id: 4,
favorite: true
}
];
let filteredProjects = projects.filter(p => p.favorite);
Obviously, you're not using a static array as I have in the example so you will need to alter it for your use case (e.g. state.projects.filter(p => p.favorite)). You could also use lodash filter if you're already using that library.

Related

Spartacus Storefront Multisite I18n with Backend

We've run into some problems for our MultiSite Spartacus setup when doing I18n.
We'd like to have different translations for each site, so we put these on an API that can give back the messages dependent on the baseSite, eg: backend.org/baseSiteX/messages?group=common
But the Spartacus setup doesn't let us pass the baseSite? We can
pass {{lng}} and {{ns}}, but no baseSite.
See https://sap.github.io/spartacus-docs/i18n/#lazy-loading
We'd could do it by overriding i18nextInit, but I'm unsure how to achieve this.
In the documentation, it says you can use crossOrigin: true in the config, but that does not seem to work. The type-checking say it's unsupported, and it still shows uw CORS-issues
Does someone have ideas for these problems?
Currently only language {{lng}} and chunk name {{ns}} are supported as dynamic params in the i18n.backend.loadPath config.
To achieve your goal, you can implement a custom Spartacus CONFIG_INITIALIZER to will populate your i18n.backend.loadPath config based on the value from the BaseSiteService.getActive():
#Injectable({ providedIn: 'root' })
export class I18nBackendPathConfigInitializer implements ConfigInitializer {
readonly scopes = ['i18n.backend.loadPath']; // declare config key that you will resolve
readonly configFactory = () => this.resolveConfig().toPromise();
constructor(protected baseSiteService: BaseSiteService) {}
protected resolveConfig(): Observable<I18nConfig> {
return this.baseSiteService.getActive().pipe(
take(1),
map((baseSite) => ({
i18n: {
backend: {
// initialize your i18n backend path using the basesite value:
loadPath: `https://backend.org/${baseSite}/messages?lang={{lng}}&group={{ns}}`,
},
},
}))
);
}
}
and provide it in your module (i.e. in app.module):
#NgModule({
providers: [
{
provide: CONFIG_INITIALIZER,
useExisting: I18nBackendPathConfigInitializer,
multi: true,
},
],
/* ... */
})
Note: the above solution assumes the active basesite is set only once, on app start (which is the case in Spartacus by default).

Improve Nuxt TTFB

I'm building a large application using Nuxt and Vuetify, everything is good and working fine but unfortunately the score from Lighthouse is not the best with only 42 in performance.
I already improved a few things like:
Better fonts loading from google;
Moving async code from nuxtServerInit to the layout;
Removing unnecessary third party services;
It went from 42 to 54 but I'm still not very happy about the result.
Unfortunately I'm not the best doing these improvements because I lack of knowledge.
I see the TTFB is not optimal at all but I don't really know what can I improve... So I hope you can help me to boost my application with hints and suggestions.
Here I will paste my nuxt.congig.js so that you're aware of what I'm using and how:
const path = require('path')
const colors = require('vuetify/es5/util/colors').default
const bodyParser = require('body-parser')
const maxAge = 60 * 60 * 24 * 365 // one year
const prefix = process.env.NODE_ENV === 'production' ? 'example.' : 'exampledev.'
const description =
'description...'
let domain
if (
process.env.NODE_ENV === 'production' &&
process.env.ENV_SLOT === 'staging'
) {
domain = 'example.azurewebsites.net'
} else if (
process.env.NODE_ENV === 'production' &&
process.env.ENV_SLOT !== 'staging'
) {
domain = 'example.com'
} else {
domain = ''
}
module.exports = {
mode: 'universal',
/**
* Disabled telemetry
*/
telemetry: false,
/*
** Server options
*/
server: {
port: process.env.PORT || 3030
},
serverMiddleware: [
bodyParser.json({ limit: '25mb' }),
'~/proxy',
'~/servermiddlewares/www.js'
],
router: {
middleware: 'maintenance'
},
env: {
baseUrl:
process.env.NODE_ENV === 'production'
? 'https://example.com'
: 'https://localhost:3030',
apiBaseUrl:
process.env.API_BASE_URL || 'https://example.azurewebsites.net'
},
/*
** Headers of the page
*/
head: {
title: 'example',
meta: [
{ charset: 'utf-8' },
{ name: 'viewport', content: 'width=device-width, initial-scale=1' },
{
hid: 'description',
name: 'description',
content: description
},
{
hid: 'fb:app_id',
property: 'fb:app_id',
content: process.env.FACEBOOK_APP_ID || 'example'
},
{
hid: 'fb:type',
property: 'fb:type',
content: 'website'
},
{
hid: 'og:site_name',
property: 'og:site_name',
content: 'example'
},
{
hid: 'og:url',
property: 'og:url',
content: 'https://example.com'
},
{
hid: 'og:title',
property: 'og:title',
content: 'example'
},
{
hid: 'og:description',
property: 'og:description',
content: description
},
{
hid: 'og:image',
property: 'og:image',
content: 'https://example.com/images/ogimage.jpg'
},
{
hid: 'robots',
name: 'robots',
content: 'index, follow'
},
{
name: 'msapplication-TileColor',
content: '#ffffff'
},
{
name: 'theme-color',
content: '#ffffff'
}
],
link: [
{
rel: 'apple-touch-icon',
sizes: '180x180',
href: '/apple-touch-icon.png?v=GvbAg4xwqL'
},
{
rel: 'icon',
type: 'image/png',
sizes: '32x32',
href: '/favicon-32x32.png?v=GvbAg4xwqL'
},
{
rel: 'icon',
type: 'image/png',
sizes: '16x16',
href: '/favicon-16x16.png?v=GvbAg4xwqL'
},
{ rel: 'manifest', href: '/site.webmanifest?v=GvbAg4xwqL' },
{
rel: 'mask-icon',
href: '/safari-pinned-tab.svg?v=GvbAg4xwqL',
color: '#777777'
},
{ rel: 'shortcut icon', href: '/favicon.ico?v=GvbAg4xwqL' },
{
rel: 'stylesheet',
href:
'https://fonts.googleapis.com/css?family=Abril+Fatface|Raleway:300,400,700&display=swap'
}
]
},
/*
** Customize the page loading
*/
loading: '~/components/loading.vue',
/*
** Global CSS
*/
css: ['~/assets/style/app.scss', 'swiper/dist/css/swiper.css'],
/*
** Plugins to load before mounting the App
*/
plugins: [
'#/plugins/axios',
'#/plugins/vue-swal',
'#/plugins/example',
{ src: '#/plugins/vue-infinite-scroll', ssr: false },
{ src: '#/plugins/croppa', ssr: false },
{ src: '#/plugins/vue-debounce', ssr: false },
{ src: '#/plugins/vue-awesome-swiper', ssr: false },
{ src: '#/plugins/vue-html2canvas', ssr: false },
{ src: '#/plugins/vue-goodshare', ssr: false }
],
/*
** Nuxt.js modules
*/
modules: [
'#/modules/static',
'#/modules/crawler',
'#nuxtjs/axios',
'#nuxtjs/auth',
'#nuxtjs/device',
'#nuxtjs/prismic',
'#dansmaculotte/nuxt-security',
'#nuxtjs/sitemap',
[
'#nuxtjs/google-analytics',
{
id: 'example',
debug: {
sendHitTask: process.env.NODE_ENV === 'production'
}
}
],
['cookie-universal-nuxt', { parseJSON: false }],
'nuxt-clipboard2'
],
/*
** Security configuration
*/
security: {
dev: process.env.NODE_ENV !== 'production',
hsts: {
maxAge: 15552000,
includeSubDomains: true,
preload: true
},
csp: {
directives: {
// removed contents
}
},
referrer: 'same-origin',
additionalHeaders: true
},
/*
** Prismic configuration
*/
prismic: {
endpoint: 'https://example.cdn.prismic.io/api/v2',
preview: false,
linkResolver: '#/plugins/link-resolver',
htmlSerializer: '#/plugins/html-serializer'
},
/*
** Auth module configuration
*/
auth: {
resetOnError: true,
localStorage: false,
cookie: {
prefix,
options: {
maxAge,
secure: true,
domain
}
},
redirect: {
callback: '/callback',
home: false
},
strategies: {
local: {
endpoints: {
login: {
url: '/auth/local',
method: 'POST',
propertyName: 'token'
},
logout: { url: '/auth/logout', method: 'POST' },
user: { url: '/me', method: 'GET', propertyName: false }
},
tokenRequired: true,
tokenType: 'Bearer'
},
google: {
client_id:
process.env.GOOGLE_CLIENT_ID ||
'example'
},
facebook: {
client_id: process.env.FACEBOOK_APP_ID || 'example',
userinfo_endpoint:
'https://graph.facebook.com/v2.12/me?fields=about,name,picture{url},email',
scope: ['public_profile', 'email']
}
}
},
/*
** Vuetify Module initialization
*/
buildModules: [
['#nuxtjs/pwa', { meta: false, oneSignal: false }],
'#nuxtjs/vuetify'
],
/*
** Vuetify configuration
*/
vuetify: {
customVariables: ['~/assets/style/variables.scss'],
treeShake: true,
rtl: false,
defaultAssets: {
font: false,
icons: 'fa'
}
},
/*
** Vue Loader configuration
*/
chainWebpack: config => {
config.plugin('VuetifyLoaderPlugin').tap(() => [
{
progressiveImages: true
}
])
},
/*
** Build configuration
*/
build: {
analyze: true,
optimizeCSS: true,
/*
** You can extend webpack config here
*/
extend(config, ctx) {
config.resolve.alias.vue = 'vue/dist/vue.common'
// Run ESLint on save
if (ctx.isDev && ctx.isClient) {
config.devtool = 'cheap-module-source-map'
config.module.rules.push({
enforce: 'pre',
test: /\.(js|vue)$/,
loader: 'eslint-loader',
exclude: /(node_modules)/,
options: {
fix: true
}
})
}
if (ctx.isServer) {
config.resolve.alias['~'] = path.resolve(__dirname)
config.resolve.alias['#'] = path.resolve(__dirname)
}
}
}
}
A few maybe useful information:
I use only scoped style for each page and component and the amount of custom style is really poor since I'm using almost everything from Vuetify as it is;
When I do "view page source" from my browser, I don't like to see a very long CSS inside the page, not minimised...
I don't load anything using fetch or asyncData, I prefer to load data once component is mounted;
Evrything is deployed on Azure and I consume a .Net core API.
What would be nice to know are the best practices with some examples to improve the performances, in particular the TTFB.
In Lighthouse I see "Remove unused JavaScript" with a list of /_nuxt/.. files... But I don't think these files are unused and so I would like to know why they are flagged like so...
Maybe Azure should clean the project on each deploy? I don't know...
I use the az Azure Cli and I deploy just by doing git push azure master, so nothing special.
"Reduce initial server response time"... How? The plan where production app is running is the faster in Azure, what should I improve and how?
"Minimize main-thread work": What does it mean?
"Reduce JavaScript execution time": How?
I hope you can help me to understand and boost everything.
I will keep this post updated with your requests, maybe you wish to see something more about the project. Thanks
I've recently had to go through this process with a rather large Nuxt application, so I can share some of the insights and solutions we came up with. We managed to bump ours up by about 40 points before we were happy.
My number one piece of advice for anyone reading: Ditch the frameworks. By design, they are bloated to handle as many common use cases as possible and make application as easy as possible, at the expense of size. In the realm of browsers, where size and speed are everything, each new framework (Nuxt, Vue, Vuetify) adds another layer of abstraction that negatively impacts size and speed.
Anyways, with that out of the way, here's some other pieces of advice for those that cannot ditch the frameworks.
Lighthouse can often be misleading
We found that the "Remove unused Javascript" warnings were basically impossible to fix with Vue. The problem is that Lighthouse is only able to inspect the code that is actually run during the test, and has no idea that code for error handling or onclick handling in the Vue runtime is necessary, until of course it is.
Unfortunately, it's not possible to know ahead of time what code in the runtime is going to be necessary, so it all needs to be sent. However, as the developer, you at least have control over what 3rd party libraries, modules, and plugins are needed during the initial load of the application. It's up to you to ensure only the necessary pieces are sent and used.
So in Lighthouses eyes, there's lots of useless, unused code. However, the second the application needs to do anything, it's no longer useless. Hence why it is somewhat misleading.
Always keep this in mind, because there's a lot of "problems" that these tools will report that are just a fact of how Javascript applications work. To me, it seems that the developers of these frameworks still have a few more hurdles to overcome in making Javascript apps truly accessible and performant in the eyes of Google.
Keep your Plugins and modules short.
Each plugin you add to your application in the nuxt.config.js increases the size of the main JS bundle included in each page. This inevitably leads to lots of unused code, huge JS file sizes, and of course, longer load times.
It's perfectly valid to instead add plugins to only the pages they're needed on:
// inside the SocialSharing.vue component
import Vue from 'vue'
import VueGoodshare from 'vue-goodshare'
Vue.use(VueGoodshare)
export default { ... }
A reminder though: The page this import happens will still have all the code from vue-goodshare added. It's much better to instead only include the components from these libraries that you actually need.
A good way to check this is running your build with the analyze property set to true. (It may be helpful for you to share your analysis here)
Reduce Initial Server Response Time
If you're already running the best server, there's still a few things you can do to help speed things up.
Leverage caching for your pages, so that there's no need to render them server side. However, some of these tests (like Lighthouse) specifically disable caching, leading to poor results.
Reduce the amount of work required to render pages. Ensure there's no blocking API calls happening, keep pages simple and small, and ensure that the server is not overloaded.
Utilize edge caching, or edge deployments, so that your application is closer to your users. For example, if your application is deployed in USWEST, and Lighthouse is being tested in Dubai, you're likely going to see a lot of latency in that request, which will drive up the server response time.
You may need to follow this up with the specific server you're running, and where it's located to get more help. However, the points I outlined would almost certainly get your TFFB to a green score.
Minimize Main Thread Work
In browsers, the main thread is where all the action happens. It is solely responsible for handling user interactions, updating the page, and in essence, turning a document of HTML into a living application. A main thread that is too busy can lead to performance problems, especially noticeable by users when they're trying to interact with your page.
Often, when seeing this, it's because you're running too much Javascript. Specifically, you're running too much Javascript all at once, which ends up blocking up the main thread. Javascript-heavy applications are notorious for this, and it can be a really challenging problem to solve.
The single biggest helper for our app was delaying the loading of unimportant scripts. For example, we run Rollbar, and Google Analytics on all our pages. Instead of loading the scripts at app-start, we instead just load their small command queues, and delay the load time of the big scripts by ~5s. This frees up the main thread to focus on more important things, like rehydrating the Vue application.
You'll also find significant savings by just reducing the amount of JS there is to process. Each line of code returned to the client is another line that has to be sent, parsed, and executed. I would definitely take a look at your modules and plugins first to see if there's some low hanging fruit.
Reduce Javascript Execution Time
This is another unfortunate metric being used, which in our test often just means "the app is still doing something". I say it's unfortunate because in our experience it did not impact the performance or user experience in the application.
We frequently saw our third party services, like Intercom, Rollbar, GA, etc, extending their execution times well past 10s, and with third party code, there's nothing you can do besides not use it.
My advice: Focus on optimizing the application using everything else I've highlighted. This is something that can be incredibly difficult to specifically fix, and is usually just a symptom of other things, such as the main thread being too busy, third part code being slow.
One Last Piece Of Advice
If all else fails, you may be able to "trick" some of the tests in your favour. We did this by delaying the load of our GA and Rollbar scripts until after the test has completed. Remember, this tool is looking at certain metrics in a certain timeframe, and scoring you based on that. You may be able to leverage simple alternate techniques, like lazy loading below the fold, to see a noticeable difference in performance.
Anyways, this is quite a complicated task, and by no means is there a "3 step guide to success" here. You'll find plenty of guides online claiming they've brought their Vue app from 30 to 100 with a few simple changes, but they all ignore the fact that real apps have a lot of code and do a lot of things, and balancing that with speed and performance is an art form.
You may want to take a look at resources such as the shell application model, or service workers.
If you need any clarification on this post, feel free to ask away. But keep in mind, the question you're asking is broad, and doesn't just have a single "right" way of approaching. It's ultimately up to you to take the important bits here and apply them as you can.
Update with examples
Most of what I've talked about has been quite hard to show examples for, as I've covered topics that are either overly simplistic and don't need an explanation, or are vague concepts to begin with. However, one method we used that had some good results can be shown.
Here's an example of a modified script we use to load Intercom:
var APP_ID = "your_app_id_here";
window.intercomSettings = {
app_id: APP_ID,
hide_default_launcher: !0,
session_duration: 36e5
},
function() {
var n,
e,
t = window,
o = t.Intercom;
"function" == typeof o ? (o("reattach_activator"), o("update", t.intercomSettings)) : (n = document, (e = function() {
e.c(arguments)
}).q = [], e.c = function(t) {
e.q.push(t)
}, t.Intercom = e, o = function() {
// Don't load the full Intercom script until after 10s
setTimeout(function() {
var t = n.createElement("script");
t.type = "text/javascript",
t.crossorigin = "anonymous",
t.async = !0,
t.src = "https://widget.intercom.io/widget/" + APP_ID;
var e = n.getElementsByTagName("script")[0];
e.parentNode.insertBefore(t, e)
}, 1e4)
}, "complete" === document.readyState ? o() : t.attachEvent ? t.attachEvent("onload", o) : t.addEventListener("load", o, !1))
This is a custom version of the script they give you to place in your apps <head></head> tag. However, you'll notice we've added a setTimeout function that will delay the loading of the full Intercom script. This gives your application a chance to load everything else without competing for network or CPU time.
However, as Intercom is no longer guaranteed to be available, you'll need to use greater caution when interacting with it.
This exact same concept can be applied to just about every 3rd party script you might load in. We also use it with Google Analytics, where we initialize the command queue, but defer loading the actual script. Obviously, this can cause tracking issues with short sessions, but that is the tradeoff you need to make if performance is your primary goal.

Specify the webpack "mainFields" on a case by case basis

Webpack has a resolve.mainFields configuration: https://webpack.js.org/configuration/resolve/#resolvemainfields
This allows control over what package.json field should be used as an entrypoint.
I have an app that pulls in dozens of different 3rd party packages. The use case is that I want to specify what field to use depending on the name of the package. Example:
For package foo use the main field in node_modules/foo/package.json
For package bar use the module field in node_modules/bar/package.json
Certain packages I'm relying on are not bundled in a correct manner, the code that the module field is pointing to does not follow these rules: https://github.com/dherman/defense-of-dot-js/blob/master/proposal.md This causes the app to break if I wholesale change the webpack configuration to:
resolve: {
mainFields: ['module']
}
The mainFields has to be set to main to currently get the app to work. This causes it to always pull in the CommonJS version of every dependency and miss out on treeshaking. Hoping to do something like this:
resolve: {
foo: {
mainFields: ['main']
},
bar: {
mainFields: ['module'],
}
Package foo gets bundled into my app via its main field and package bar gets bundled in via its module field. I realize the benefits of treeshaking with the bar package, and I don't break the app with foo package (has a module field that is not proper module syntax).
One way to achieve this would be instead of using resolve.mainFields you can make use of resolve.plugins option and write your own custom resolver see https://stackoverflow.com/a/29859165/6455628 because by using your custom resolver you can programmatically resolve different path for different modules
I am copy pasting the Ricardo Stuven's Answer here
Yes, it's possible. To avoid ambiguity and for easier implementation,
we'll use a prefix hash symbol as marker of your convention:
require("#./components/SettingsPanel");
Then add this to your configuration file (of course, you can refactor
it later):
var webpack = require('webpack');
var path = require('path');
var MyConventionResolver = {
apply: function(resolver) {
resolver.plugin('module', function(request, callback) {
if (request.request[0] === '#') {
var req = request.request.substr(1);
var obj = {
path: request.path,
request: req + '/' + path.basename(req) + '.js',
query: request.query,
directory: request.directory
};
this.doResolve(['file'], obj, callback);
}
else {
callback();
}
});
}
};
module.exports = {
resolve: {
plugins: [
MyConventionResolver
]
}
// ...
};
resolve.mainFields not work in my case, but resolve.aliasFields works.
More details in https://stackoverflow.com/a/71555568/7534433

react-data-grid headerRenderer Sorting

How is sorting and filtering implemented when using a custom header cell in the react-data-grid?
{
key: "myColumn1",
name: "My Column Name",
headerRenderer: this.renderHeader,
sortable: true,
filterable: true
},
{
key: "myColumn2",
name: "My Other Column Name",
sortable: true,
filterable: true
}
...
renderHeader(props){
return (<extra-stuff>
{props.column.name}
</extra-stuff>)
}
I have myColumn2 sorting fine but nothing happens with the custom header: myColumn1. What do I need to do in renderHeader(){} to get sorting (and filtering, but mainly sorting) working?
Instead of trying to call a function in headerRenderer, put a component instead:
{
key: "myColumn1",
name: "My Column Name",
headerRenderer: <renderHeader value={this.renderHeader()} />,
sortable: true,
filterable: true
},
Looks like a solution was checked-in for this almost a year ago. I do work on an intranet and don't have direct internet access for development so maybe i have an older version. Since I can't get the version in my environment I'll wait for someone to verify that the latest version works as expected before I accept my own answer (or I'll try to get the latest version with the change in there.)
One other thing I will have to do with the way the solution was implemented is stop propagation of the click event because in my custom header i have a ReactIcon that is clickable to change from status wording to a menu. So, if they click right on the icon in the header do the name/menu change and if they click anywhere else in the header then do the sort. I'll have to look up how to do that in React.
Update
I took the changes from the check-in in the link above and added them to my react-data-grid.js and it works great. I.e. I had to add the changes to the pure javascript version of the code that npm pulled into our node_modules.
I also did have to use e.stopPropagation() call in my headerRenderer like i thought i'd have to so that clicking the icon i have in the headerRenderer would not sort when click (but just do a different operation) but it was easy to add.
I do have one question about the change i made to my js which here is a piece of the change...
return React.createElement(
'div',
{className: className,
onClick: this.onClick,
style: {cursor: 'pointer'} },
React.createElement(
'span',
{ className: 'pull-right' },
this.getSortByText()
),
//change was here from just: this.props.column.name
this.props.column.headerRenderer? this.props.column.headerRenderer(this.props) : this.props.column.name
);
How would i have done this in JavaScript so that it would work whether my headerRenderer was ()=>{ return <span>...</span> } or <MyComponent /> ???
Since i always use functions for my headerRenderer i just did the change so that it worked the one way but would like to know how to make the change so it works both ways. Something like this? ...
this.props.column.headerRenderer?
(isFunction(this.props.column.headerRenderer)? this.props.column.headerRenderer(this.props) : React.createElement(this.props.column.headerRenderer, {...this.props}) )
:
this.props.column.name
????

Configurable redirect URL in DocPad

I'm using DocPad to generate system documentation. I am including release notes in the format
http://example.com/releases/1.0
http://example.com/releases/1.1
http://example.com/releases/1.2
http://example.com/releases/1.3
I want to include a link which will redirect to the most recent release.
http://example.com/releases/latest
My question: how do I make a link that will redirect to a relative URL based on configuration? I want this to be easily changeable by a non-programmer.
Update: I've added cleanurls into my docpad.js, similar to example below. (see code below). But using "grunt docpad:generate" seems to skip making the redirect (is this an HTML page?). I've a static site. I also confirmed I'm using the latest cleanurls (2.8.1) in my package.json.
Here's my docpad.js
'use strict';
var releases = require('./releases.json'); // list them as a list, backwards: ["1.3", "1.2", "1.1", "1.0"]
var latestRelease = releases.slice(1,2)[0];
module.exports = {
outPath: 'epicenter/docs/',
templateData: {
site: {
swiftype: {
apiKey: 'XXXX',
resultsUrl: '/epicenter/docs/search.html'
},
ga: 'XXXX'
},
},
collections: {
public: function () {
return this.getCollection('documents').findAll({
relativeOutDirPath: /public.*/, isPage: true
});
}
},
plugins: {
cleanurls: {
simpleRedirects: {'/public/releases/latest': '/public/releases/' + latestRelease}
},
lunr: {
resultsTemplate: 'src/partials/teaser.html.eco',
indexes: {
myIndex: {
collection: 'public',
indexFields: [{
name: 'title',
boost: 10
}, {
name: 'body',
boost: 1
}]
}
}
}
}
};
When I run grunt docpad:generate, my pages get generated, but there is an error near the end:
/data/jenkins/workspace/stage-epicenter-docs/docs/docpad/node_modules/docpad-plugin-cleanurls/node_modules/taskgroup/node_modules/ambi/es6/lib/ambi.js:5
export default function ambi (method, ...args) {
^^^^^^
I can't tell if that's the issue preventing this from running but it seems suspicious.
Providing that your configuration is available to the DocPad Configuration File, you can use the redirect abilities of the cleanurls plugin to accomplish this for both dynamic and static environments.
With a docpad.coffee configuration file, it would look something like this:
releases = require('./releases.json') # ['1.0', '1.1', '1.2', '1.3']
latestRelease = releases.slice(-1)[0]
docpadConfig =
plugins:
cleanurls:
simpleRedirects:
'/releases/latest': '/releases/' + latestRelease
module.exports = docpadConfig

Resources