I used a Gatsby starter for my static site, and one of the pages included in that starter is a demo page with all of the UI elements.
I want to keep the page (so I can copy and paste from the demo) but don't want to be publicly available. How do I "unpublish" without deleting the file?
Is there a way to tell gatsby-node.js to skip that page when generating the public facing site?
There are a bunch of Gatsby Node API helpers that you can use, one being deletePage.
If you have a page src/pages/demo.js, this will delete that page during creation.
// gatsby-node.js
exports.onCreatePage = async ({ page, actions: { deletePage } }) => {
if (page.path.match(/^\/demo/)) {
deletePage(page)
}
}
Lots of good options here, just wanna throw my hat in the ring for plugin options that prevent pages from being created in the first place:
If it is a page component, i.e inside src/pages folder, gatsby use a plugin called gatsby-plugin-page-creator to generate page, and it recently accept a ignore patterns.
There's a caveat, the built-in gatsby-plugin-page-creator doesn't take user options, so we'd have to rename the pages folder to ignore it.
root
└── src
- └── pages
+ └── screens <-- rename
└── index.js
└── ignore-file-name.js
And then in gatsby-config.js:
module.exports = {
plugins: [
{
resolve: `gatsby-plugin-page-creator`,
options: {
path: `${__dirname}/src/screens`,
ignore: [`ignore-file-name.js`],
},
},
]
}
If it is a programmatically page generated from markdown or json, you might be able to ignore it in gatsby-source-file-system, as pointed out in this github comment.
The example there even ignores file based on environment, which is more useful since you can still see your reference during development, but it won't show in build.
{
resolve: `gatsby-source-filesystem`,
options: {
path: `${__dirname}/content`,
ignore: process.env.NODE_ENV === `production` && [`**/draft-*`]
}
}
An alternative would be to add a "published" attribute to the frontmatter assuming it's a markdown page that could be a true or false value. Then add logic into your gatsby-node.js file which looks at this to determine whether or not to run createPage().
Related
I have a problem, when I click to go to the /analytics page on my site, adblockers block the analytics.json file that's being requested by Next.js as they think it's an analytics tracker (it's not, it's a page listing analytics products).
Is there a way to rename the route files Next.js uses when navigating to server-side rendered pages on the client-side?
I want to either obfuscate the names so they're not machine readable, or have a way to rename them all.
Any help appreciated.
With thanks to #gaston-flores I've managed to get something working.
In my instance /analytics is a dynamic page for a category, so I moved my pages/[category]/index.tsx file to pages/[category]/category.tsx and added the following rewrite:
// next.config.js
module.exports = {
async rewrites() {
return [
{
source: "/:category",
destination: "/:category/category",
},
];
},
};
This now gets the category.json file rather than analytics.json, which passes the adblockers checks and renders as expected.
Note that due to having a dynamic file name in the pages/[category] directory (pages/[category]/[product].tsx), I had to move that to pages/[category]/product/[product].tsx as I was seeing the /analytics page redirected to /analytics/category for some reason without this tweak.
I have a react app (using create react app) and then a component library (using webpack directly). One component in the component library needs to load a png file in one of the components that it exports. In the webpack config for the component library I have a section such as:
{
test: /\.(png|jpg|jpeg|gif)$/i,
type: 'asset/resource',
},
This successfully results in a file like: 29b6b66cf1a691be2b3f.png in the component libraries output directory. The issue, is that when the application uses that component and the component attempts to load the image, the img element is <img ... src="29b6b66cf1a691be2b3f.png" /> and that fails to load, since that image actually lives in the component library folder (within the react application's node_modules/component-library/ at that point).
I have scoured the internet to the best of my ability, and can not seem to figure out what the best solution would be here. Any help is appreciated. I will quickly offer clarification if needed.
UPDATE: I have discovered CopyWebpackPlugin but it is quite unfortunate that this would require me to eject the "parent" application from create react app. Something I would very much prefer to avoid.
UPDATE2: Current plan is to try following something like what is explained here. The jist of it is to utilize something like rewire to avoid needing to eject and still be able to edit the webpack config via something like:
// in ./build.js
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
// edit webpack config values here
... I will answer my own question here if I find that this approach works.
Alright, well this felt like it was much messier than it needed to be, but I was able to figure out a way to accomplish this. The pieces were as follows.
First, needed to install copy-webpack-plugin. This was not as simple as it might sound, because I needed to find one that would not conflict with the version of webpack required by my react-scripts (create react app). I determined that copy-webpack-plugin#6.4.1 was the last version to support webpack v4, so I installed that and then added the following to my package.json:
"overrides": {
"copy-webpack-plugin": {
"webpack": "4.44.2"
}
},
this ensured that it would use the version of webpack that my react-scripts installation was expecting.
Then, I also needed to install rewire, and add the following to a file called build.js:
const rewire = require('rewire');
const defaults = rewire('react-scripts/scripts/build.js');
const config = defaults.__get__('config');
const CopyPlugin = require("copy-webpack-plugin");
const patterns = [
{
context: "node_modules/component-library/path/",
from: "*.png",
to: "."
},
]
if(config.plugins === undefined){
config.plugins = [new CopyPlugin({patterns})]
}else{
config.plugins.push(new CopyPlugin({patterns}))
}
// below lets it work in dev mode too
if(config.devServer === undefined){
config.devServer = {
writeToDisk: true
}
}else{
config.devServer.writeToDisk = true;
}
Finally, had to update my build script to be:
"scripts": {
...
"build": "node ./build.js",
...
},
My Gatsby site sources its content from YAML files rather than Markdown. There are a bunch of categories and subcategories, such that valid paths are like:
mysite.com/movies/drama/war/1980s
mysite.com/video-games/puzzles
etc...
I would like to create an index.js for every subdirectory: /movies , /movies/drama, /movies/drama/war, /video-games, etc.
My strategy is to get a list of all the pages, from which I can extract all the unique paths and createPage an index.js in each. However I tried querying
{
allSitePage {
nodes {
path
}
}
}
during gatsby-node's createPages but none of the paths had been created yet -- only the root and 404 paths existed (probably because these are the only files in my /pages directory?)
I'm guessing there's a better way to do this anyway. How can I create an index.js in every subdirectory? Thank you.
EDIT: I realized createPage is an asyncronous function, my query was running before the createPages had completed. I adjusted all my code with async/await to ensure the pages were created prior to running the allSitePage query -- but it still comes up empty. Now really confused. (Another note: in the GraphiQL browser, the query of course shows all the pages. They just don't seem to be available until after gatsby-node.js completes)
2nd EDIT: I was asked to post my createPage() code:
const postsYaml = resultYaml.data.allYaml.edges
// 1-1 mapping between YAML edge and Gatsby page
// use Promise.all() to ensure all pages are created before continuing
await Promise.all(postsYaml.map(async (post, index) => {
const previous = index === postsYaml.length - 1 ? null : postsYaml[index + 1].node
const next = index === 0 ? null : postsYaml[index - 1].node
createPage({
path: post.node.fields.slug,
component: blog-page,
context: {
slug: post.node.fields.slug,
previous,
next
},
})
}))
So this code runs, creates the pages, then I execute the GraphQL I originally posted -- and the pages aren't there.
I am using create-react-app as starter kit and was having a go at antd component library.
I can't seem to find a way to customize their styles (for branding purpose). Documentation doesn't seem to give clear directions regarding customization.
Docs link: https://ant.design/docs/react/customize-theme
They suggest two ways to do it though :
1.Using theme property . But this only works for antd-init or dva-cli boilerplates and not for create-react-app
2.Overriding Less variables .
Now to make the either of these option work for create-react-app without eject , what are the steps I need to take?
You question is not very well researched I am afraid. At a minimum, read the obvious resurces like the instructions at https://ant.design/docs/react/use-with-create-react-app and older Stackoverflow questions (like How do I use .babelrc to get babel-plugin-import working for antd?) before posting a question.
As it stands, CRA does not support changing the Babel configuration. This is needed to include the antd babel-plugin, which is needed to support importing only the necessary antd modules.
As a result you can only import the whole monolithic antd in a non-ejected CRA app.
if you don't intend to use eject you should use config-overrides.js to specify how you want to modify the default webpack.config.js that is part of react-scripts. Then once you have a config-overrides you can add a less Rule that has an option for modifyVars. Here you can specify any changes to the less variables.
function addLessRule(config, env)
{
const { getLoader, loaderNameMatches } = require("react-app-rewired");
const fileLoader = getLoader(
config.module.rules,
rule => loaderNameMatches(rule, 'file-loader')
);
const lessExtension = /\.less$/;
fileLoader.exclude.push(lessExtension);
const cssRules = getLoader(
config.module.rules,
rule => String(rule.test) === String(/\.css$/)
);
var lessModifyVars = {
'primary-color': '#990000',
'link-color': '#1DA57A',
'border-radius-base': '2px',
'layout-header-background' : '#f0f2f5'
};
var lessLoaders = [
{ loader: 'style-loader'} ,
{ loader: 'css-loader' },
{ loader: 'less-loader', options: {
modifyVars: lessModifyVars
} }
];
lessRule = {
test : lessExtension,
use: lessLoaders,
}
config.module["rules"].push( lessRule );
return config
}
If you want to customize antd without using react eject and don't want to change use forked versions of create-react-app or react scripts then
you can simply create a less file (let's say main.less ) , import antd.less and replace the default variables of ant design in this main.less file.
Now compile this less file using lessc (npm i -g less).
lessc "main.less antd.css" --js
--js is for inline javascript in less
Now simply include these complied css in your app.
take a look at https://medium.com/#aksteps/782c53cbc03b
I am in to developing a large client side app with very complex views on each modules using Extjs5. I have developed apps in Extjs but they all compile to a single app.js file. So based on the complexity of the views in all the app mockups I am estimating the size of the app will be around 20MB to 25MB even after compiled.
I was thinking to split the modules as separate applications and create a master app with tabs or something, which triggered will be loading individual apps in a iFrame within the master app. But I doubt if the iframe behaviors are altered in different browsers or deprecated in any future browser releases, that will be another big problem.
So is there any way in sencha cmd, which compiles app in separate files based on modules and load them on demand out of the box ?
If not what is the advisable solution I should be going ahead with.
Starting with Sencha Cmd 6.5 you can split your code into multiple files. To achieve this, you have to split your code into exjts packages if it’s not already done:
In the end, you should have a similar folder structure to this:
workspaceDir
|
+->appA
+->appB
+->packages
|
+-> local
|
+->CoreComponents
+->ProcurementModule
+->ForumModule
+->BOMModule
+->ReportModule
In your app.json file you could add/move your packages from requires to uses. As a last step you have to add the new package-loader to the requires array in app.json.
You end up with something like that:
{
// [...]
"uses": [
"ProcurementModule",
"ForumModule",
"BOMModule",
"ReportModule"
],
"requires": [
"CoreComponents",
"package-loader"
]
// [...]
}
Next you need to start your Sencha Cmd build with the additional flag -uses.
If you do this, Sencha Cmd will build your optional packages first and add them to the resource folder in your build output directory.
sencha app build -uses production
It is important, that you don't have any references to classes in optional packages from your main application. Otherwise your build will fail.
Since your optional packages are not loaded automatically on page startup you need to trigger the loading process manually. I do it usually within the routing process of my AppControllers.
Here an example:
Ext.define('MyApp.view.main.MainController', {
extend: 'Ext.app.ViewController',
requires: [
'Ext.Package'
],
routes: {
'forum': {
before: 'loadForum',
action: 'showView'
}
},
loadForum(action) {
if (Ext.Package.isLoaded('ForumModule')) {
action.resume();
} else {
//Loading mask code here [...]
Ext.defer(() => { // it needs some time to show up the loading mask
Ext.Package.load('ForumModule').then(() => {
//Remove loading mask code here [...]
action.resume(); //proceed router process; all package files loaded
});
}, 500);
}
},
showView() {
this.getView().add({xclass: 'ForumModule.view.MainView'});
}
});
More information on this topic:
http://docs.sencha.com/cmd/guides/whats_new_cmd65.html#whats_new_cmd65_-_dynamic_package_loading