Skip to content

Commit 0b8d237

Browse files
committed
init
1 parent 0c84a43 commit 0b8d237

File tree

15 files changed

+450
-0
lines changed

15 files changed

+450
-0
lines changed

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,9 @@
1+
# Custom
2+
test/
3+
test-*/
4+
example/script.min.js
5+
example/style.min.css
6+
17
# Byte-compiled / optimized / DLL files
28
__pycache__/
39
*.py[cod]

README.rst

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
******
2+
Eleran
3+
******
4+
5+
Simple & fast sass compiler and javascript minifier with hot reloading
6+
7+
Install
8+
=======
9+
10+
Install using pip::
11+
12+
pip install eleran
13+
14+
Usage
15+
=====
16+
17+
Command::
18+
19+
eleran <command> <path> --mode=<mode>
20+
21+
**commad:**
22+
23+
- watch
24+
25+
- validate
26+
27+
- generate
28+
29+
**--mode:**
30+
31+
- debug
32+
33+
Example, generate eleran.json to example directory::
34+
35+
eleran generate example
36+
37+
Watch::
38+
39+
eleran watch example
40+
41+
Configuration File
42+
==================
43+
44+
eleran.json::
45+
46+
[
47+
{
48+
"sass": {
49+
"source": "sass/style.scss",
50+
"output": "style.min.css",
51+
"output_style": "compressed",
52+
"source_comments": false
53+
}
54+
},
55+
{
56+
"js": {
57+
"include": [
58+
"js/foo.js",
59+
"js/bar.js"
60+
],
61+
"output": "script.min.js"
62+
}
63+
}
64+
]
65+
66+
**Sass - output_style:**
67+
68+
- compact
69+
70+
- compressed
71+
72+
- expanded
73+
74+
- nested

eleran/__init__.py

Whitespace-only changes.

eleran/__main__.py

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
from .main import cli
2+
3+
if __name__ == '__main__':
4+
cli()

eleran/main.py

Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
from sys import argv
2+
from os import path, getcwd
3+
from watchgod import watch
4+
from watchgod.watcher import DefaultDirWatcher
5+
from json import loads as load_json, dumps as dump_json
6+
from uuid import uuid4
7+
from traceback import format_exc as get_traceback
8+
from .version import VERSION_STRING
9+
from jsmin import jsmin
10+
from sass import compile as sass_compile
11+
import click
12+
import re
13+
14+
class ValidationError(Exception):
15+
def __init__(self, message):
16+
super().__init__(message)
17+
18+
class EleranWatcher(DefaultDirWatcher):
19+
def should_watch_file(self, entry):
20+
return entry.name.endswith(('.scss', '.js', '.sass', '.json'))
21+
22+
class Eleran():
23+
def __init__(self, TargetDir=None, Mode=None):
24+
self.BasePath = getcwd()
25+
self.WatchFiles = []
26+
self.ConfigFile = "eleran.json"
27+
self.Config = {}
28+
self.Index = {}
29+
self.Version = VERSION_STRING
30+
self.Mode = Mode
31+
32+
# Path
33+
if TargetDir:
34+
self.BasePath = path.join(getcwd(), TargetDir)
35+
36+
# Print
37+
echo_click(" * Eleran version", self.Version)
38+
39+
def read_file(self, Filename, Mode="r"):
40+
FileObject = open(Filename, Mode)
41+
String = FileObject.read()
42+
return String
43+
44+
def get_id(self, Filepath):
45+
ID = self.Index.get(Filepath)
46+
return ID
47+
48+
def get_sass_imported(self, Source, ID):
49+
String = self.read_file(Source)
50+
Result = re.findall('^@import\s*".+"', String, re.MULTILINE|re.IGNORECASE)
51+
for Item in Result:
52+
Filename = Item.replace(" ", "")
53+
Filename = Filename.replace('@import"', "")
54+
Filename = Filename[:-1]
55+
Filename = "_" + Filename + ".scss"
56+
Filepath = path.join(path.dirname(Source), Filename)
57+
if path.isfile(Filepath):
58+
if not Filepath in self.WatchFiles:
59+
self.Index[Filepath] = ID
60+
self.WatchFiles.append(Filepath)
61+
62+
def watch(self):
63+
# Print
64+
echo_click(" * Watch for", self.BasePath, Color="green")
65+
echo_click(" * Press CTRL+C to quit", Color="green")
66+
67+
# Config File
68+
ConfigFile = path.join(self.BasePath, self.ConfigFile)
69+
70+
# Watch
71+
for Changes in watch(self.BasePath, watcher_cls=EleranWatcher):
72+
_Type, FileChange = list(Changes)[0]
73+
TypeChange = _Type.name.capitalize()
74+
if FileChange in self.WatchFiles:
75+
echo_click(" *", TypeChange, FileChange)
76+
ID = self.get_id(FileChange)
77+
if self.Mode == "debug":
78+
echo_click(" * Build ID:", ID)
79+
# Build
80+
self.build(ID)
81+
elif FileChange == ConfigFile:
82+
echo_click(" *", TypeChange, FileChange)
83+
self.reload_config()
84+
85+
def build(self, ID):
86+
try:
87+
ConfigData = self.Config.get(ID)
88+
ConfigType = ConfigData.get("Type")
89+
90+
if ConfigType == "sass":
91+
SassSource = ConfigData.get("source")
92+
SassOutput = ConfigData.get("output")
93+
SassStyle = ConfigData.get("output_style")
94+
SassComment = ConfigData.get("source_comments")
95+
Filepath = path.join(self.BasePath, SassSource)
96+
String = sass_compile(
97+
filename = Filepath,
98+
output_style = SassStyle,
99+
source_comments = SassComment
100+
)
101+
102+
# Detect new import
103+
self.get_sass_imported(Filepath, ID)
104+
105+
# Save
106+
FileWrite = open(path.join(self.BasePath, SassOutput), "w")
107+
FileWrite.write(String)
108+
FileWrite.close()
109+
110+
elif ConfigType == "js":
111+
JSInclude = ConfigData.get("include")
112+
JSOutput = ConfigData.get("output")
113+
JSString = ""
114+
115+
# Join
116+
for i in JSInclude:
117+
Filepath = path.join(self.BasePath, i)
118+
String = self.read_file(Filepath)
119+
JSString += String
120+
121+
# Save
122+
JSString = jsmin(JSString)
123+
FileWrite = open(path.join(self.BasePath, JSOutput), "w")
124+
FileWrite.write(JSString)
125+
FileWrite.close()
126+
127+
except Exception as e:
128+
echo_click(" * Error:", e, Color="red")
129+
130+
def generate_config(self):
131+
Sample = [
132+
{
133+
"sass": {
134+
"source": "sass/style.scss",
135+
"output": "style.min.css",
136+
"output_style": "compressed",
137+
"source_comments": False
138+
}
139+
},
140+
{
141+
"js": {
142+
"include": [
143+
"js/fo.js",
144+
"js/bar.js"
145+
],
146+
"output": "script.min.js"
147+
}
148+
}
149+
]
150+
151+
# Save
152+
String = dump_json(Sample, indent="\t")
153+
Filepath = path.join(self.BasePath, "eleran.json")
154+
155+
# Print
156+
echo_click(" * Creating config file to", Filepath)
157+
158+
# Save
159+
FileWrite = open(Filepath, "w")
160+
FileWrite.write(String)
161+
FileWrite.close()
162+
163+
def reload_config(self):
164+
self.Index = {}
165+
self.WatchFiles = []
166+
self.Config = {}
167+
self.load_config()
168+
169+
def load_config(self):
170+
# Config
171+
ConfigFile = path.join(self.BasePath, self.ConfigFile)
172+
173+
# Print
174+
echo_click(" * Loading config:", ConfigFile)
175+
176+
# Is file exist
177+
if path.isfile(ConfigFile):
178+
# Load file
179+
Config = self.read_file(ConfigFile)
180+
Config = load_json(Config)
181+
182+
for Item in Config:
183+
SassConfig = Item.get("sass")
184+
JSConfig = Item.get("js")
185+
ID = str(uuid4())
186+
if SassConfig:
187+
# Config
188+
SassConfig["Type"] = "sass"
189+
self.Config[ID] = SassConfig
190+
191+
# File
192+
File = SassConfig.get("source")
193+
if File:
194+
Filepath = path.join(self.BasePath, File)
195+
if path.isfile(Filepath):
196+
self.get_sass_imported(Filepath, ID)
197+
self.Index[Filepath] = ID
198+
self.WatchFiles.append(Filepath)
199+
200+
else:
201+
echo_click(" *", File, "not found", Color="red")
202+
else:
203+
echo_click(" * Sass source file not found", Color="red")
204+
205+
elif JSConfig:
206+
# Config
207+
JSConfig["Type"] = "js"
208+
self.Config[ID] = JSConfig
209+
210+
# Files
211+
Files = JSConfig.get("include")
212+
if Files:
213+
for File in Files:
214+
Filepath = path.join(self.BasePath, File)
215+
if path.isfile(Filepath):
216+
self.Index[Filepath] = ID
217+
self.WatchFiles.append(Filepath)
218+
else:
219+
echo_click(" *", File, "not found", Color="red")
220+
else:
221+
echo_click(" * JS include files not found", Color="red")
222+
else:
223+
echo_click(" * Unknown config type", Color="red")
224+
else:
225+
raise ValidationError("Config file not found")
226+
227+
def echo_click(*args, Color=None):
228+
Text = []
229+
for i in args:
230+
Text.append(str(i))
231+
Text = " ".join(Text)
232+
if Color:
233+
click.echo(click.style(Text, fg=Color))
234+
else:
235+
click.echo(Text)
236+
237+
@click.command()
238+
@click.argument('command', default='watch')
239+
@click.argument('target', default='')
240+
@click.option('--mode', default='')
241+
def cli(command, target, mode):
242+
try:
243+
# App
244+
App = Eleran(target, mode)
245+
246+
# Command
247+
if command == "watch":
248+
App.load_config()
249+
App.watch()
250+
elif command == "validate":
251+
App.load_config()
252+
echo_click(" * Config validation success!", Color="green")
253+
elif command == "generate":
254+
App.generate_config()
255+
echo_click(" * Success!", Color="green")
256+
257+
except KeyboardInterrupt:
258+
print(" * Exit")
259+
260+
except Exception as e:
261+
if mode == "debug":
262+
echo_click(" * " + str(get_traceback()), Color="red")
263+
else:
264+
echo_click(" * Error: " + str(e), Color="red")

eleran/version.py

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
from distutils.version import StrictVersion
2+
3+
__all__ = ['VERSION']
4+
5+
VERSION_STRING = '1.0.0'
6+
7+
VERSION = StrictVersion(VERSION_STRING)

example/eleran.json

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
[
2+
{
3+
"sass": {
4+
"source": "sass/style.scss",
5+
"output": "style.min.css",
6+
"output_style": "compressed",
7+
"source_comments": false
8+
}
9+
},
10+
{
11+
"js": {
12+
"include": [
13+
"js/foo.js",
14+
"js/bar.js"
15+
],
16+
"output": "script.min.js"
17+
}
18+
}
19+
]

example/js/app.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
var Greeting = Hi + Name;

example/js/bar.js

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
var Name = "Bapakode!";
2+
var Greeting = Hi + Name;

example/js/foo.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
var Hi = "Hi, my name is ";

0 commit comments

Comments
 (0)