0.9.5 • Published 3 years ago

dunya v0.9.5

Weekly downloads
2
License
MIT
Repository
github
Last release
3 years ago

Dunya.js

Dunya.js is a simple tool for setting up a better workflow.

Installation

npm install --save dunya

Setup

Add this script to your package.json:

{
  "scripts": {
    "dev": "dunya dev"
  },
  ...
}

Further installation option

Additionally you can install dunya globally:

npm install -g dunya

and run the cli directly:

dunya dev

Getting started

Dunya is a tool you can use to build a custom workflow. It works as following:

First you'll have a input directory. Your source code will go in there. If you run the dev script it will apply all changed of your code in a output directory. Then it will run a live server by default in the output directory. In the process of applying your changes however, it will pipe your file changes through a so called pipeline. This pipeline can be manipulated using plugins.

To get started, run this command:

npm run dev
# or
dunya dev

Whenever you make a change in you input directory the default pipeline will apply the change in the output directory. So by default there is not much going on but it will quickly get more interesting when using plugins.

A file called dunya.config.json should get created. This is the file where you can configure dunya.js. Additionally you can set some parameters by using command line arguments. See more at Configuration.

Plugins

Let's start adding plugins in our project. Go to your dunya.config.json and start add our first plugin. You need to create an array called plugins:

{
  "plugins": [...],
  ...
}

Dunya comes with a hand full of default plugins (See more default plugins). For example we can add sass support to our pipeline like following:

{
  "plugins": ["#sass-support"],
  ...
}

Using the # we can import plugins that comes with dunya by default.

Note that plugins can overwrite each other when they have the same name (Not the name you entered in the dunya.config.json) This can be used to e.g. overwrite the default plugin more about the default plugin here

Non-default plugins

You can also import plugins from external sources. Let's say there is a plugin in the npm packages called foo-bar-support that you want to install. Simple call

npm i --save foo-bar-support

and add this plugin like you would use the require function or the import statement in javascript / typescript:

{
  "plugins": ["foo-bar-support"],
  ...
}

Own plugins

If you want to create a own plugin simple create a javascript file in your directory. Then you need to import the plugin using a tilde:

{
  "plugins": ["~/path/to/plugins.js"],
  ...
}

More about creating a plugin here.

Default plugins

Here is a list of all default plugins that come with dunya:

sass-support:

propertyvalue
namesass-support
priority300

This will pipe all .scss files and compile them to regular .css files. Note that you need to add the proper file name everywhere you want to link a stylesheet even though the file extension differs in the input directory. For example:

<!-- wrong -->
<link rel="stylesheet" href="styles/main.scss" />

<!-- right -->
<link rel="stylesheet" href="styles/main.css" />

template

propertyvalue
nametemplate
priority200

The template plugin will effect every .html file. Your first have to create a template.html file directly in the input directory. Now the template.html will replace all .html files. Now let's see what the template plugin provides:

This template will wrap every .html file in this boilerplate html:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Test</title>
  </head>
  <body>
    {{ html }}
  </body>
</html>

For example:

<h1>Hello, world!</h1>

will get converted to:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>Test</title>
  </head>
  <body>
    <h1>Hello, world!</h1>
  </body>
</html>

Additionally you have the variable relativePath which points to the root directory of your html. You can also use a tilde to go to the root directory automatically. Note: Behind the tilde syntax is a regex that searches for the specific combination "~/.

For example:

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

<script>
  var relativePath = '{{relativePath}}';
</script>
...

will get converted to:

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

<script>
  var relativePath = '../..';
</script>
...

There is also a function called load. This will load another file in your input directory.

Let's say you want to add a header to all your html files:

...
<body>
  {{ load('components/header.html') }} {{ html }}
</body>
...

Additionally you'll have the variable path which is the path to the original html file.

enhancedRouting

propertyvalue
nameenhancedRouting
priority240

This plugin will take all of your .html files and move them into their own directory.

So this input routing:

input
|- About.html
|- Contact.html
|- index.html

will get converted to:

output
|- About
|   |- index.html
|- Contact
|   |- index.html
|- index.html

This improves the routing of your website because now you can use localhost:8080/About instead of localhost:8080/About.html.

default

propertyvalue
namedefault
priority100

The default plugin contains all necessary code for the scripts to work. The idea behind having a default plugin is that you can overwrite it to your needs. Let's say your unhappy how to default plugin handles the watcher. Then you could create a new plugin with a higher priority than 100 or you could create a plugin called default and overwrite the setupWatcher function.

The default plugins sets the following functions read more about all functions here:

fsRead: reads a file at path synced.

fsWrite: writes the file to path with the content fileContent synced.

It also called the fsMkdirs function so it wont fail when the path doesn't exist.

fsMkdirs: creates all directories at dirs synced.

fsRemove: removes a file or directory at path.

fsEmpty: creates and empties the directory path.

fsIsDir: returns true when path leads to a directory.

fsExists: return true when path exists.

fsReadJSON: calls fsRead and returns the content as JSON. If the parsing fails it will throw an error.

setupWatcher: ensures that all directories exist, initiates a watcher using the npm package sane, and makes sure that the event is called properly as well as all files are getting added when the watcher got setup.

deleteEvent: calls fsRemove at path.

addDirEvent: calls fsMkdirs at path.

addFileEvent: calls fsWrite at path.

changeFileEvent: calls fsWrite at path.

startServer: starts a live server using the npm package live-server.

Configuration

Here is a list of all configuration options dunya provides per default:

jsonclidefaultdescription
plugins-'#default'an array of the plugins
inputDir-inputDir'input'the path to the input directory
outputDir-outputDir'output'the path to the output directory
ip-ip'127.0.0.1'the ip address of the server
port-port8080the port of the server
noWatcher-noWatcherundefinedstarts the script without a watcher
noServer-noServerundefinedstarts the script without a server

Creating a plugin

Dunya starts to get very powerful when adding personalized plugins. Let's start by creating our own plugin. Make sure dunya is installed in your project. Then create a file e.g. custom-plugin.js. We need to export an object that has at least the property name but it should also have a priority and functionality:

exports.default = {
  name: 'custom plugin', // the name must not be the same as the file name

  priority: 300,

  fileEvent(path, fileContent) {
    console.log(`'${path}' got changed`);
    return false;
  },
};

Now we just need to import our plugin in our dunya.config.json:

{
  "plugins": ["~/custom-plugin.js"],
  ...
}

Using typescript

If you want to have autocomplete consider using typescript. Keep in mind that you have to compile your typescript file as commonjs. Simply make sure that this property is in your tsconfig.json:

{
  "compilerOptions": {
    "module": "commonjs",
    ...
  },
  ...
}

This typescript example implements the same features like the javascript variant but it's using the DunyaPlugin interface for autocomplete:

// import the interface for autocomplete
import DunyaPlugin from 'dunya/scripts-ts/DunyaPlugin';

const plugin: DunyaPlugin = {
  name: 'custom plugin',
  priority: 300,
};

plugin.fileEvent = function (path: string, fileContent: string): boolean {
  console.log(`'${path}' got changed`);
  return false;
};

export default plugin;

Priority

To set the right priority to your plugins is important for plugins to work together. Here are some norms you can follow so your plugin does not interfere with other plugins:

prioritywhen to use
>400Only events that does not influence anything and does not stop any function
>300When a file format gets converted e.g. scss -> css
>200When a file gets moved or edited but not the file format it self e.g. a minimizer
<100When another event gets appended that does not influence the content or path of the file

Converting files

Let's say we want to add a support for a language like sass. This will give you a vague idea how to implement something like that.

First we want to est the filePipe function:

plugin.filePipe = function (pipe: { path: string; fileContent: string }): { path: string; fileContent: string } {
  // First we check if the file ends with .scss
  if (filterExtensionName(pipe.path)) return;

  // Then we compile the content of the file
  pipe.fileContent = compile(pipe.path, pipe.fileContent);

  // We change the extension from .scss to .css
  pipe.path = convertPath(pipe.path);

  // And finally, we return the pipe
  return pipe;
};

But we should not forget to set the deleteEventPipe incase someone deletes a .scss file.

plugin.deleteEventPipe = function (pipe: IOPaths): IOPaths {
  // First we check again if the file ends with .scss
  if (filterExtensionName(pipe.outputPath)) return;

  //Then we only have to change the extension from .scss to .css since there is no content because the file got deleted.
  pipe.outputPath = convertPath(pipe.outputPath);

  // And then we return the pipe
  return pipe;
};

Calling methods

There are different types of method how a function of a plugin can be called:

namedescription
PluginCallerWill call every plugin until one returns a truthy value
PluginCallAllWill call every plugin regardless of the return value
PluginGetterWill call every plugin until one returns a value that is not undefined
PluginPipeWill call all plugins. Every plugin has to option to modify the argument 'pipe'. If one returns undefined the pipe will not change

Plugin functions

The best way to get an overview of all the possible ways to interact with dunya is to look at the typescript template itself.

Here is some definition of some terms:

iopath

iopath is an object with three entries:

export interface IOPaths {
  path: string;
  inputPath: string;
  outputPath: string;
}

E.g. we change a file called foo/bar.js:

pathinputPathoutputPath
foo/bar.jsinput/foo/bar.jsoutput/foo/bar.js

Note that the input and output directories will differ when we configure it differently read more about configuration.