How to programmatically render a stylus folder? - node.js

I want to programmatically do the following (located in package.json - essentially, renders the .styl documents in the "assets" folder and saves them as CSS files)
"scripts": {
"stylus": "stylus assets --compress"
}
Currently I do the following:
import { exec } from 'child_process'
await exec('npm run stylus')
While this does work, I want to know if there is a better way of doing this, and I want it so that I can execute this multiple times later on in the code, as it isn't necessarily for pre-rendering

Related

NOde.js/Express App can't find some node_modules

I use several Node/Express modules in my app, and everything works fine for every module as long as I do const module = require('module');. I don't need to define a static path for these modules as app.use(express.static(path.join(__dirname, 'public')));.
However, for the sweetalert module, if I define in my layout.pug (base pug file) script(src="/node_modules/sweetalert/dist/sweetalert.min.js"), I get a 404 Error (not found) unless I include in app.js the following static path: app.use("/node_modules", express.static(__dirname + "/node_modules"));.
My question is: is this the normal behaviour or is it something I'm not doing right? (I'm kinda confused why I have to define a static path just for one of several modules I use.
Here's whats going on:
app.use(express.static(path.join(__dirname, 'public'))); is declaring that the public directory is accessible to the browser. You should put all your front end resources in that folder. This will help separate what can be accessed from the server and what can be accessed from the client.
When you reference script(src="/node_modules/sweetalert/dist/sweetalert.min.js") the browser throws a 404 because that file is not located in the public directory, therefore off limits to the browser.
Adding this line app.use("/node_modules", express.static(__dirname + "/node_modules")); "fixes" your issue but now exposes all your node_modules to the browser. This probably isn't a good idea and I'm sure a security expert could elaborate why this shouldn't be done.
How I would resolve this issue: Go through your .pug code and look at any resources your front end requires. Then copy them over to the public folder and fix your references to use the copy of the resource.
Here's an example of a script I use to move resources from the node_module directory to a public/assets directory:
build.js:
const path = require('path');
var fs = require('fs');
const ASSETS = [
'jquery/dist/jquery.min.js',
'sweetalert/dist/sweetalert.min.js'
];
if (!fs.existsSync('./public/assets')){
fs.mkdirSync('./public/assets');
}
ASSETS.map(asset => {
let filename = asset.substring(asset.lastIndexOf("/") + 1);
let from = path.resolve(__dirname, `./node_modules/${asset}`)
let to = path.resolve(__dirname, `./public/assets/${filename}`)
if (fs.existsSync(from)) {
fs.createReadStream(from).pipe(fs.createWriteStream(to));
} else {
console.log(`${from} does not exist.\nUpdate the build.js script with the correct file paths.`)
process.exit(1)
}
});
then I update my package.json to include this in the scripts:
package.json:
"scripts": {
"build": "node ./build.js || true",
"start": "node ./bin/www"
}
then in any of my views pages I reference the resource by using the new path
random.pug:
script(src="/assets/jquery.min.js")
script(src="/assets/sweetalert.min.js")
Finally before you deploy your app you now must run the following command:
npm run build then npm start
You will only need to run the build command if your front end resources change. So if you only ever use sweetalert.min.js you will only need to run the build the first time you run your app. If later on you add another resource aNewResource.js you will need to update the build.js file and run npm run build again.

Sharing code between React Native + Node

I am using React Native and Node.js. I want to share code between the two. My folder structure is as so.
myreactnativeapp/
mynodeserver/
myshared/
In the react native and node apps I have included the
package.json
"dpendencies" : {
"myshared": "git+https://myrepository/ugoshared.git"
}
This can then be included in each project via require/import etc. This all works fine and for production I'm happy with it. (Though I'd love to know a better way?)
The issue I'm facing is in development it's really slow.
The steps for a change to populate are:
Make changes in Shared
Commit Changes to git
Update the npm module
In development, I really want the same codebase to be used rather than this long update process. I tried the following:
Adding a symlink in node_models/shared - doesn't work in react-native package mangaer
Using relative paths ../../../shared - doesn't work in react-native package mangaer
Any other ideas?
Update 1
I created a script.sh which I run to copy the files into a local directory before the package manager starts. It's not ideal but at least I only have to restart the packager instead of messing with git etc.
#myreactnativeapp/start.sh
SOURCE=../myshared
MODULE=myshared
rm -rf ./$MODULE
mkdir ./$MODULE
find $SOURCE -maxdepth 1 -name \*.js -exec cp -v {} "./$MODULE/" \;
# create the package.json
echo '{ "name": "'$MODULE'" }' > ./$MODULE/package.json
# start the packager
node node_modules/react-native/local-cli/cli.js start
Then in my package.json I update the script to
"scripts": {
"start": "./start.sh",
},
So, the process is now.
Make a change
Start/Resetart the packager
Automatic:
Script copies all .js files under myshared/ -> myreactnativeapp/myshared/
Script creates a package.json with the name of the module
Because I've added the package.json to the copied files with the name of the module, in my project I can just include the items the same as I would if the module was included via the package manager above. In theory when I switch to using the package in production I wont have to change anything.
Import MyModule from 'myshared/MyModule'
Update 2
My first idea got tiresome restarting the package manager all the time. Instead i created a small node script in the shared directory to watch for changes. Whenever there is a change it copies it to the react native working directory.
var watch = require('node-watch')
var fs = require('fs')
var path = require('path')
let targetPath = '../reactnativeapp/myshared/'
watch('.', { recursive: false, filter: /\.js$/ }, function(evt, name) {
console.log('File changed: '+name+path.basename(__filename))
// don't copy this file
if(path.basename(__filename) === name) {
return
}
console.log(`Copying file: ${name} --> ${targetPath+name}`);
fs.copyFile(name, targetPath+name, err => {
if(err) {
console.log('Error:', err)
return;
}
console.log('Success');
})
});
console.log(`Starting to watch: ${__dirname}. All files to be copied to: ${targetPath}`)

Is there a way to automatically copy files to wwwroot?

I have my index.html in the directory called wwwroot and it's being accessed from the browser on locahost:5001. Whe nI installed some packages using NPM, the directory node_modules was placed on the same level as wwwroot.
When I'm linking to the files, I use a relative path like this.
href="../node_modules/package_this_or_that/package.min.js"
It seems to me that a better approach would be to have those delivered to the wwwroot directory and have them reside there. Not all the contents of the packages, just the files that are actually being used (skipping readmes etc.).
Is there a package for that? Or is it something that needs to be done using a build script?
This answer recommends using GULP which seems dated now.
You are not supposed to access node_modules files from front-end like your html or cshtml files. So you are right you should copy them to the wwwroot folder.
You can use grunt as linked in Tseng comment but I personally prefer Gulp, I think it's much quicker and easier to use.
Your package.json file:
{
"version": "1.0.0",
"name": "asp.net",
"private": true,
"devDependencies": {
"gulp": "3.9.1",
"gulp-cached": "1.1.0",
}
}
Then create a gulpfile.js at your project's root level and you can write something like
var gulp = require('gulp'),
cache = require('gulp-cached'); //If cached version identical to current file then it doesn't pass it downstream so this file won't be copied
gulp.task('default', 'copy-node_modules');
gulp.task('copy-node_modules', function () {
try {
gulp.src('node_modules/**')
.pipe(cache('node_modules'))
.pipe(gulp.dest('wwwroot/node_modules'));
}
catch (e) {
return -1;
}
return 0;
});
Finally open the Task Runner Explorer (if you are using Visual Studio) and execute either your default task or directly the copy-node_modules task.
Gulp is very useful, I suggest you explore other different gulp tasks. You can concat and minify both CSS and JS files, remove comments, you can even create a watch task that executes other tasks as soon as a file changes.

jspm bundle code in subdirectory that is served as root

I am trying to create a self-executing bundle of an application, but jspm can't find what it's looking for.
I have the following folder structure
The src directory contains all of the JavaScript, but it is hosted by node as if it were the root. jspm_packages is hosted as if it were inside the root, making normal module import without a path possible (ie import React from 'react')
The app runs just fine, but when I try to build it fails because it doesn't know to look in the src directory and the jspm_packages directory for modules. Is there a way to fix this without changing the folder structure or the root-hosting?
I am ok with moving the system.config.js file into src if that makes this possible)
EDIT
This is easy if you move jspm_packages into src.
in package.json
"jspm": {
"directories": {
"baseURL": "src"
},
"configFile": "src/system.config.js"
}
This will put both system.config.js and jspm_packages in src (don't use a baseUrl in system.config.js), and bundling will work. The major drawback here is the src folder no longer contains only the project code; it now also contains library code. Performing folder searches becomes harder, and I just prefer the idea of a folder with all of my code in it.
EDIT2
After thinking about this problem more, I guess what I am really after is a method to specify an alternate path configuration during bundling. Based on my reading of the docs, this appears to be unsupported.
You can set your baseUrl to be the root (i.e. "/") and then set the path for your source code on the paths property like this:
System.config({
baseURL: "/",
paths: {
"*": "dist/*",
"github:*": "jspm_packages/github/*",
"npm:*": "jspm_packages/npm/*"
}
})
Full working example can be seen here.
Furthermore, if you need to change you paths just for bundling you could use the jspm-cli in a gulp task and override your builder configuration like this:
var jspm = require('jspm');
gulp.task('task', function () {
var builder = new jspm.Builder();
builder.loadConfigSync('pathToYourConfigFile');
builder.config({
paths: {
'*': 'pathToYourCode'
}
});
builder.bundle('yourOptionsHere');
}

how to require a specific file using duojs

I need to include a library that is present on github, but is not well-packaged; using Duo.js
At the moment of writing I am using the following to achieve what I desire:
bower
gulp
main-bower-files
Bower just downloades the library.
Gulp, with main-bower-files are useful to override the single package options and setup a so-called "main file" that I can build.
Example:
gulp.task('copy-libs', function () {
return gulp.src(bowerFiles({ env: 'development' }))
.pipe(gulp.dest('build/libs/'));
});
bower.json file:
"dependencies": {
"cash": "sudo-js/cash",
"bootstrap": "~3.3.2",
"delorean": "~0.8.7",
"react": "~0.12.2"
},
"overrides": {
"cash": {
"main": {
"development": "build/debug/cash.js"
}
}
}
}
How can i achieve this with duojs?
The documentation is quite thin regarding libraries that does not ship with a valid component.json
You can specify the path to an entry file for your lib. It won't be as clean as just specifying user/repo, but it'll get the job done.
For example, when including Twitter Bootstrap from twbs/bootstrap
require('twbs/bootstrap#v3.3.2:dist/js/bootstrap.js');
// repo: twbs/bootstrap
// version/tag: v3.3.2
// path: dist/js/bootstrap.js
Unfortunately, this doesn't work out-of-the-box since it assumes you have the jQuery global... so you need to add this above the previous line.
jQuery = require('components/jquery'); // leave out `var` so it becomes a global
This includes jQuery from the wonderful components project. (they package up popular libs so they can be consumed by various package managers.
Also, it turns out there is a components/bootstrap that is properly packaged with a component.json.
So, you can actually make bootstrap work with the following:
jQuery = require('components/jquery');
require('components/bootstrap');
For the other libraries that aren't as common, you can use the process mentioned first to specify the path to the right JS/CSS file. (ie: user/repo#version:path)

Resources