@browsersync/bslive 0.0.5 → 0.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/Cargo.toml +32 -18
- package/bin.js +6 -0
- package/bslive/Cargo.toml +35 -0
- package/{build.rs → bslive/build.rs} +1 -1
- package/bslive/src/lib.rs +130 -0
- package/bsnext/Cargo.toml +21 -0
- package/bsnext/src/main.rs +110 -0
- package/crates/bsnext_client/Cargo.toml +8 -0
- package/crates/bsnext_client/build.rs +53 -0
- package/crates/bsnext_client/dist/index.js +6493 -0
- package/crates/bsnext_client/generated/dto.ts +116 -0
- package/crates/bsnext_client/generated/schema.ts +187 -0
- package/crates/bsnext_client/index.html +14 -0
- package/crates/bsnext_client/package-lock.json +2059 -0
- package/crates/bsnext_client/package.json +25 -0
- package/crates/bsnext_client/src/lib.rs +1 -0
- package/crates/bsnext_client/style.css +3 -0
- package/crates/bsnext_client/ts/console.ts +25 -0
- package/crates/bsnext_client/ts/index.ts +73 -0
- package/crates/bsnext_client/tsconfig.json +16 -0
- package/crates/bsnext_core/Cargo.toml +43 -0
- package/crates/bsnext_core/src/dir_loader.rs +230 -0
- package/crates/bsnext_core/src/dto.rs +281 -0
- package/crates/bsnext_core/src/handlers/mod.rs +1 -0
- package/crates/bsnext_core/src/handlers/proxy.rs +95 -0
- package/crates/bsnext_core/src/lib.rs +11 -0
- package/crates/bsnext_core/src/meta/mod.rs +5 -0
- package/crates/bsnext_core/src/not_found/mod.rs +2 -0
- package/crates/bsnext_core/src/not_found/not_found.html +20 -0
- package/crates/bsnext_core/src/not_found/not_found_service.rs +41 -0
- package/crates/bsnext_core/src/not_found/route_list.rs +49 -0
- package/crates/bsnext_core/src/panic_handler.rs +32 -0
- package/crates/bsnext_core/src/raw_loader.rs +226 -0
- package/crates/bsnext_core/src/server/actor.rs +92 -0
- package/crates/bsnext_core/src/server/error.rs +41 -0
- package/crates/bsnext_core/src/server/handler_change.rs +85 -0
- package/crates/bsnext_core/src/server/handler_listen.rs +157 -0
- package/crates/bsnext_core/src/server/handler_patch.rs +42 -0
- package/crates/bsnext_core/src/server/handler_routes_updated.rs +27 -0
- package/crates/bsnext_core/src/server/handler_stop.rs +38 -0
- package/crates/bsnext_core/src/server/mod.rs +10 -0
- package/crates/bsnext_core/src/server/router/mod.rs +112 -0
- package/crates/bsnext_core/src/server/router/tests.rs +204 -0
- package/crates/bsnext_core/src/server/signals.rs +11 -0
- package/crates/bsnext_core/src/server/state.rs +19 -0
- package/crates/bsnext_core/src/servers_supervisor/actor.rs +199 -0
- package/crates/bsnext_core/src/servers_supervisor/file_changed_handler.rs +41 -0
- package/crates/bsnext_core/src/servers_supervisor/get_servers_handler.rs +23 -0
- package/crates/bsnext_core/src/servers_supervisor/input_changed_handler.rs +21 -0
- package/crates/bsnext_core/src/servers_supervisor/mod.rs +6 -0
- package/crates/bsnext_core/src/servers_supervisor/start_handler.rs +82 -0
- package/crates/bsnext_core/src/servers_supervisor/stop_handler.rs +26 -0
- package/crates/bsnext_core/src/ws/mod.rs +164 -0
- package/crates/bsnext_example/Cargo.toml +10 -0
- package/crates/bsnext_example/src/basic.rs +51 -0
- package/crates/bsnext_example/src/lib.rs +26 -0
- package/crates/bsnext_example/src/lit.rs +37 -0
- package/crates/bsnext_example/src/md.rs +18 -0
- package/crates/bsnext_fs/Cargo.toml +22 -0
- package/crates/bsnext_fs/src/actor.rs +122 -0
- package/crates/bsnext_fs/src/buffered_debounce.rs +166 -0
- package/crates/bsnext_fs/src/filter.rs +30 -0
- package/crates/bsnext_fs/src/inner_fs_event_handler.rs +94 -0
- package/crates/bsnext_fs/src/lib.rs +141 -0
- package/crates/bsnext_fs/src/remove_path_handler.rs +46 -0
- package/crates/bsnext_fs/src/stop_handler.rs +15 -0
- package/crates/bsnext_fs/src/stream.rs +167 -0
- package/crates/bsnext_fs/src/test/mod.rs +213 -0
- package/crates/bsnext_fs/src/watch_path_handler.rs +67 -0
- package/crates/bsnext_fs/src/watcher.rs +348 -0
- package/crates/bsnext_input/Cargo.toml +22 -0
- package/crates/bsnext_input/src/input_test/mod.rs +151 -0
- package/crates/bsnext_input/src/lib.rs +153 -0
- package/crates/bsnext_input/src/md.rs +541 -0
- package/crates/bsnext_input/src/paths.rs +64 -0
- package/crates/bsnext_input/src/route.rs +185 -0
- package/crates/bsnext_input/src/route_manifest.rs +186 -0
- package/crates/bsnext_input/src/server_config.rs +88 -0
- package/crates/bsnext_input/src/target.rs +7 -0
- package/crates/bsnext_input/src/watch_opt_test/mod.rs +68 -0
- package/crates/bsnext_input/src/yml.rs +1 -0
- package/crates/bsnext_output/Cargo.toml +16 -0
- package/crates/bsnext_output/src/json.rs +11 -0
- package/crates/bsnext_output/src/lib.rs +25 -0
- package/crates/bsnext_output/src/pretty.rs +147 -0
- package/crates/bsnext_resp/Cargo.toml +13 -0
- package/crates/bsnext_resp/src/js/snippet.html +1 -0
- package/crates/bsnext_resp/src/js/ws.js +1 -0
- package/crates/bsnext_resp/src/lib.rs +79 -0
- package/crates/bsnext_system/Cargo.toml +29 -0
- package/crates/bsnext_system/src/args.rs +43 -0
- package/crates/bsnext_system/src/lib.rs +227 -0
- package/crates/bsnext_system/src/monitor.rs +241 -0
- package/crates/bsnext_system/src/monitor_any_watchables.rs +127 -0
- package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test-2.snap +11 -0
- package/crates/bsnext_system/src/start_kind/snapshots/bsnext_system__start_kind__start_from_paths__test__test.snap +29 -0
- package/crates/bsnext_system/src/start_kind/start_from_example.rs +49 -0
- package/crates/bsnext_system/src/start_kind/start_from_inputs.rs +67 -0
- package/crates/bsnext_system/src/start_kind/start_from_paths.rs +51 -0
- package/crates/bsnext_system/src/start_kind.rs +56 -0
- package/crates/bsnext_system/src/startup.rs +51 -0
- package/crates/bsnext_tracing/Cargo.toml +10 -0
- package/crates/bsnext_tracing/src/lib.rs +89 -0
- package/examples/basic/input.yml +5 -0
- package/examples/basic/public/index.html +15 -0
- package/examples/basic/public/reset.css +52 -0
- package/examples/basic/public/script.js +1 -0
- package/examples/basic/public/styles.css +3 -0
- package/examples/kitchen-sink/a-file.md +1 -0
- package/examples/kitchen-sink/api/1.json +1 -0
- package/examples/kitchen-sink/api/2.json +3 -0
- package/examples/kitchen-sink/app.js +1 -0
- package/examples/kitchen-sink/input.html +185 -0
- package/examples/kitchen-sink/input.yml +133 -0
- package/examples/kitchen-sink/styles-2.css +3 -0
- package/examples/lit/index.html +21 -0
- package/examples/lit/input.yml +5 -0
- package/examples/lit/lit.js +82 -0
- package/examples/lit/package-lock.json +62 -0
- package/examples/lit/package.json +15 -0
- package/examples/md-single/frontmatter.md +35 -0
- package/examples/md-single/md-single.md +35 -0
- package/examples/openai/.nvm +1 -0
- package/examples/openai/build.mjs +21 -0
- package/examples/openai/index.html +13 -0
- package/examples/openai/input.yml +59 -0
- package/examples/openai/package-lock.json +720 -0
- package/examples/openai/package.json +21 -0
- package/examples/openai/src/index.js +21 -0
- package/examples/openai/sse/01.txt +8 -0
- package/examples/proxy-simple/input.yml +9 -0
- package/examples/single/input.yaml +26 -0
- package/index.d.ts +3 -1
- package/index.js +3 -2
- package/package.json +22 -19
- package/run.sh +6 -0
- package/src/lib.rs +0 -9
|
@@ -0,0 +1,348 @@
|
|
|
1
|
+
use crate::inner_fs_event_handler::InnerChangeEvent;
|
|
2
|
+
use notify::event::{
|
|
3
|
+
AccessKind, AccessMode, CreateKind, DataChange, MetadataKind, ModifyKind, RemoveKind,
|
|
4
|
+
RenameMode,
|
|
5
|
+
};
|
|
6
|
+
use notify::EventKind;
|
|
7
|
+
use std::collections::HashSet;
|
|
8
|
+
use std::ffi::OsStr;
|
|
9
|
+
use std::path::{Component, Path};
|
|
10
|
+
use std::sync::Arc;
|
|
11
|
+
use tokio::sync::broadcast;
|
|
12
|
+
|
|
13
|
+
pub fn create_watcher(
|
|
14
|
+
sender: Arc<broadcast::Sender<InnerChangeEvent>>,
|
|
15
|
+
cwd: &Path,
|
|
16
|
+
) -> notify::Result<notify::FsEventWatcher> {
|
|
17
|
+
let cwd_c = cwd.to_owned();
|
|
18
|
+
notify::recommended_watcher(move |res: Result<notify::Event, _>| {
|
|
19
|
+
let span = tracing::span!(tracing::Level::TRACE, "raw");
|
|
20
|
+
let _guard = span.enter();
|
|
21
|
+
match res {
|
|
22
|
+
Ok(event) => {
|
|
23
|
+
if event.paths.iter().any(|p| {
|
|
24
|
+
is_ignored_path_type(&p.as_path())
|
|
25
|
+
|| is_auto_excluded(&cwd_c.as_path(), &p.as_path())
|
|
26
|
+
}) {
|
|
27
|
+
tracing::trace!(?event.paths, "ignored!!!");
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
let msg = InnerChangeEvent {
|
|
32
|
+
absolute_path: event.paths.first().unwrap().into(),
|
|
33
|
+
};
|
|
34
|
+
match event.kind {
|
|
35
|
+
EventKind::Any => {}
|
|
36
|
+
EventKind::Access(ac) => match ac {
|
|
37
|
+
AccessKind::Any => tracing::trace!("EventKind::Access AccessKind::Any"),
|
|
38
|
+
AccessKind::Read => {
|
|
39
|
+
tracing::trace!("EventKind::Access AccessKind::Read")
|
|
40
|
+
}
|
|
41
|
+
AccessKind::Open(o) => match o {
|
|
42
|
+
AccessMode::Any => {
|
|
43
|
+
tracing::trace!(
|
|
44
|
+
"EventKind::Access AccessKind::Open AccessMode::Any"
|
|
45
|
+
)
|
|
46
|
+
}
|
|
47
|
+
AccessMode::Execute => {
|
|
48
|
+
tracing::trace!(
|
|
49
|
+
"EventKind::Access AccessKind::Open AccessMode::Execute"
|
|
50
|
+
)
|
|
51
|
+
}
|
|
52
|
+
AccessMode::Read => {
|
|
53
|
+
tracing::trace!(
|
|
54
|
+
"EventKind::Access AccessKind::Open AccessMode::Read"
|
|
55
|
+
)
|
|
56
|
+
}
|
|
57
|
+
AccessMode::Write => {
|
|
58
|
+
tracing::trace!(
|
|
59
|
+
"EventKind::Access AccessKind::Open AccessMode::Write"
|
|
60
|
+
)
|
|
61
|
+
}
|
|
62
|
+
AccessMode::Other => {
|
|
63
|
+
tracing::trace!(
|
|
64
|
+
"EventKind::Access AccessKind::Open AccessMode::Other"
|
|
65
|
+
)
|
|
66
|
+
}
|
|
67
|
+
},
|
|
68
|
+
AccessKind::Close(c) => match c {
|
|
69
|
+
AccessMode::Any => {
|
|
70
|
+
tracing::trace!(
|
|
71
|
+
"EventKind::Access AccessKind::Close AccessMode::Any"
|
|
72
|
+
)
|
|
73
|
+
}
|
|
74
|
+
AccessMode::Execute => {
|
|
75
|
+
tracing::trace!(
|
|
76
|
+
"EventKind::Access AccessKind::Close AccessMode::Execute"
|
|
77
|
+
)
|
|
78
|
+
}
|
|
79
|
+
AccessMode::Read => {
|
|
80
|
+
tracing::trace!(
|
|
81
|
+
"EventKind::Access AccessKind::Close AccessMode::Read"
|
|
82
|
+
)
|
|
83
|
+
}
|
|
84
|
+
AccessMode::Write => {
|
|
85
|
+
tracing::trace!(
|
|
86
|
+
"EventKind::Access AccessKind::Close AccessMode::Write"
|
|
87
|
+
)
|
|
88
|
+
}
|
|
89
|
+
AccessMode::Other => {
|
|
90
|
+
tracing::trace!(
|
|
91
|
+
"EventKind::Access AccessKind::Close AccessMode::Other"
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
},
|
|
95
|
+
AccessKind::Other => {
|
|
96
|
+
tracing::trace!("EventKind::Access AccessKind::Other")
|
|
97
|
+
}
|
|
98
|
+
},
|
|
99
|
+
EventKind::Create(c) => match c {
|
|
100
|
+
CreateKind::Any => tracing::trace!("EventKind::Create CreateKind::Any"),
|
|
101
|
+
CreateKind::File => {
|
|
102
|
+
tracing::trace!("EventKind::Create CreateKind::File")
|
|
103
|
+
}
|
|
104
|
+
CreateKind::Folder => {
|
|
105
|
+
tracing::trace!("EventKind::Create CreateKind::Folder")
|
|
106
|
+
}
|
|
107
|
+
CreateKind::Other => {
|
|
108
|
+
tracing::trace!("EventKind::Create CreateKind::Other")
|
|
109
|
+
}
|
|
110
|
+
},
|
|
111
|
+
EventKind::Modify(modify) => match modify {
|
|
112
|
+
ModifyKind::Any => tracing::trace!("EventKind::Modify ModifyKind::Any"),
|
|
113
|
+
ModifyKind::Data(data) => match data {
|
|
114
|
+
DataChange::Any => {
|
|
115
|
+
tracing::trace!(
|
|
116
|
+
"EventKind::Modify ModifyKind::Data DataChange::Any"
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
DataChange::Size => {
|
|
120
|
+
tracing::trace!(
|
|
121
|
+
"EventKind::Modify ModifyKind::Data DataChange::Size"
|
|
122
|
+
);
|
|
123
|
+
}
|
|
124
|
+
DataChange::Content => {
|
|
125
|
+
tracing::trace!(
|
|
126
|
+
"EventKind::Modify ModifyKind::Data DataChange::Content"
|
|
127
|
+
);
|
|
128
|
+
match sender.send(msg) {
|
|
129
|
+
Ok(_) => {}
|
|
130
|
+
Err(e) => tracing::error!(?e),
|
|
131
|
+
};
|
|
132
|
+
}
|
|
133
|
+
DataChange::Other => {
|
|
134
|
+
tracing::trace!(
|
|
135
|
+
"EventKind::Modify ModifyKind::Data DataChange::Other"
|
|
136
|
+
);
|
|
137
|
+
}
|
|
138
|
+
},
|
|
139
|
+
ModifyKind::Metadata(meta) => {
|
|
140
|
+
match meta {
|
|
141
|
+
MetadataKind::Any => {
|
|
142
|
+
tracing::trace!(
|
|
143
|
+
"EventKind::Modify ModifyKind::Metadata MetadataKind::Any"
|
|
144
|
+
);
|
|
145
|
+
match sender.send(msg) {
|
|
146
|
+
Ok(_) => {}
|
|
147
|
+
Err(e) => tracing::error!(?e),
|
|
148
|
+
};
|
|
149
|
+
}
|
|
150
|
+
MetadataKind::AccessTime => {
|
|
151
|
+
tracing::trace!("EventKind::Modify ModifyKind::Metadata MetadataKind::AccessTime")
|
|
152
|
+
}
|
|
153
|
+
MetadataKind::WriteTime => {
|
|
154
|
+
tracing::trace!("EventKind::Modify ModifyKind::Metadata MetadataKind::WriteTime")
|
|
155
|
+
}
|
|
156
|
+
MetadataKind::Permissions => {
|
|
157
|
+
tracing::trace!("EventKind::Modify ModifyKind::Metadata MetadataKind::Permissions")
|
|
158
|
+
}
|
|
159
|
+
MetadataKind::Ownership => {
|
|
160
|
+
tracing::trace!("EventKind::Modify ModifyKind::Metadata MetadataKind::Ownership")
|
|
161
|
+
}
|
|
162
|
+
MetadataKind::Extended => {
|
|
163
|
+
tracing::trace!("EventKind::Modify ModifyKind::Metadata MetadataKind::Extended")
|
|
164
|
+
}
|
|
165
|
+
MetadataKind::Other => {
|
|
166
|
+
tracing::trace!("EventKind::Modify ModifyKind::Metadata MetadataKind::Other")
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
ModifyKind::Name(mode) => match mode {
|
|
171
|
+
RenameMode::Any => {
|
|
172
|
+
tracing::trace!(
|
|
173
|
+
"EventKind::Modify ModifyKind::Name RenameMode::Any"
|
|
174
|
+
)
|
|
175
|
+
}
|
|
176
|
+
RenameMode::To => {
|
|
177
|
+
tracing::trace!("EventKind::Modify ModifyKind::Name RenameMode::To")
|
|
178
|
+
}
|
|
179
|
+
RenameMode::From => {
|
|
180
|
+
tracing::trace!(
|
|
181
|
+
"EventKind::Modify ModifyKind::Name RenameMode::From"
|
|
182
|
+
)
|
|
183
|
+
}
|
|
184
|
+
RenameMode::Both => {
|
|
185
|
+
tracing::trace!(
|
|
186
|
+
"EventKind::Modify ModifyKind::Name RenameMode::Both"
|
|
187
|
+
)
|
|
188
|
+
}
|
|
189
|
+
RenameMode::Other => {
|
|
190
|
+
tracing::trace!(
|
|
191
|
+
"EventKind::Modify ModifyKind::Name RenameMode::Other"
|
|
192
|
+
)
|
|
193
|
+
}
|
|
194
|
+
},
|
|
195
|
+
ModifyKind::Other => {
|
|
196
|
+
tracing::trace!("EventKind::Modify ModifyKind::Other")
|
|
197
|
+
}
|
|
198
|
+
},
|
|
199
|
+
EventKind::Remove(remove) => match remove {
|
|
200
|
+
RemoveKind::Any => tracing::trace!("EventKind::Remove RemoveKind::Any"),
|
|
201
|
+
RemoveKind::File => {
|
|
202
|
+
tracing::trace!("EventKind::Remove RemoveKind::File")
|
|
203
|
+
}
|
|
204
|
+
RemoveKind::Folder => {
|
|
205
|
+
tracing::trace!("EventKind::Remove RemoveKind::Folder")
|
|
206
|
+
}
|
|
207
|
+
RemoveKind::Other => {
|
|
208
|
+
tracing::trace!("EventKind::Remove RemoveKind::Other")
|
|
209
|
+
}
|
|
210
|
+
},
|
|
211
|
+
EventKind::Other => tracing::trace!("EventKind::Other"),
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
Err(e) => {
|
|
215
|
+
tracing::error!("fswadtcher {:?}", e);
|
|
216
|
+
}
|
|
217
|
+
}
|
|
218
|
+
})
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
#[cfg(test)]
|
|
222
|
+
mod test {
|
|
223
|
+
use super::*;
|
|
224
|
+
#[test]
|
|
225
|
+
fn test() {
|
|
226
|
+
let cwd = Path::new("/Users/shaneosbourne/WebstormProjects/browsersync.github.io");
|
|
227
|
+
let change = Path::new("/Users/shaneosbourne/WebstormProjects/browsersync.github.io/node_modules/ajv/dist/vocabularies/applicator/if.d.ts");
|
|
228
|
+
let excluded = is_auto_excluded(&cwd, &change);
|
|
229
|
+
assert_eq!(excluded, true);
|
|
230
|
+
}
|
|
231
|
+
}
|
|
232
|
+
|
|
233
|
+
fn is_ignored_path_type<P: AsRef<Path>>(subject: &P) -> bool {
|
|
234
|
+
subject
|
|
235
|
+
.as_ref()
|
|
236
|
+
.as_os_str()
|
|
237
|
+
.as_encoded_bytes()
|
|
238
|
+
.ends_with(b"~")
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
fn is_auto_excluded<P: AsRef<Path>>(cwd: &P, subject: &P) -> bool {
|
|
242
|
+
// todo: allow more config here...
|
|
243
|
+
let excluded: HashSet<&OsStr> = [
|
|
244
|
+
"node_modules",
|
|
245
|
+
".git",
|
|
246
|
+
".husky",
|
|
247
|
+
".vscode",
|
|
248
|
+
".idea",
|
|
249
|
+
".sass-cache",
|
|
250
|
+
]
|
|
251
|
+
.into_iter()
|
|
252
|
+
.map(OsStr::new)
|
|
253
|
+
.collect();
|
|
254
|
+
let rel = subject.as_ref().strip_prefix(cwd.as_ref());
|
|
255
|
+
return rel
|
|
256
|
+
.map(|p| match p.components().next() {
|
|
257
|
+
None => false,
|
|
258
|
+
Some(Component::Normal(str)) => excluded.contains(str),
|
|
259
|
+
Some(Component::Prefix(_)) => unreachable!("here? Prefix"),
|
|
260
|
+
Some(Component::RootDir) => unreachable!("here? RootDir"),
|
|
261
|
+
Some(Component::CurDir) => unreachable!("here? CurDir"),
|
|
262
|
+
Some(Component::ParentDir) => unreachable!("here? ParentDir"),
|
|
263
|
+
})
|
|
264
|
+
.unwrap_or(false);
|
|
265
|
+
}
|
|
266
|
+
|
|
267
|
+
#[test]
|
|
268
|
+
fn matches_single_glob() {
|
|
269
|
+
use glob_match::glob_match;
|
|
270
|
+
let p1 = "public/css/style.css";
|
|
271
|
+
let g = "public/css/*.css";
|
|
272
|
+
let actual = glob_match(g, p1);
|
|
273
|
+
assert_eq!(actual, true);
|
|
274
|
+
}
|
|
275
|
+
#[test]
|
|
276
|
+
fn matches_relative_glob_2() {
|
|
277
|
+
use glob_match::glob_match;
|
|
278
|
+
let relative_path = [
|
|
279
|
+
(
|
|
280
|
+
"/qwe/qwe/qwe/qw/ebuild/app-debug/debugger/debugger.css",
|
|
281
|
+
true,
|
|
282
|
+
),
|
|
283
|
+
("/qwe/qwe/qwe/qw/ebuild/app-debug/oops.txt", true),
|
|
284
|
+
];
|
|
285
|
+
let glob = "**/*.{css,txt}";
|
|
286
|
+
for (path, expected) in relative_path {
|
|
287
|
+
let actual = glob_match(glob, path);
|
|
288
|
+
assert_eq!(actual, expected);
|
|
289
|
+
}
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
#[test]
|
|
293
|
+
fn matches_absolute_glob() {
|
|
294
|
+
use glob_match::glob_match;
|
|
295
|
+
|
|
296
|
+
let test_cases = vec![
|
|
297
|
+
(
|
|
298
|
+
"/absolute/path/public/css/style.css",
|
|
299
|
+
"/absolute/path/public/css/*.css",
|
|
300
|
+
true,
|
|
301
|
+
),
|
|
302
|
+
(
|
|
303
|
+
"/absolute/path/public/js/script.js",
|
|
304
|
+
"/absolute/path/public/css/*.css",
|
|
305
|
+
false,
|
|
306
|
+
),
|
|
307
|
+
(
|
|
308
|
+
"/absolute/path/public/css/nested/style.css",
|
|
309
|
+
"/absolute/path/public/css/**/*.css",
|
|
310
|
+
true,
|
|
311
|
+
),
|
|
312
|
+
(
|
|
313
|
+
"/absolute/path/public/js/nested/script.js",
|
|
314
|
+
"/absolute/path/public/css/**/*.css",
|
|
315
|
+
false,
|
|
316
|
+
),
|
|
317
|
+
(
|
|
318
|
+
"/users/documents/reports/january/cash_flow.xls",
|
|
319
|
+
"/users/documents/reports/**/*.xls",
|
|
320
|
+
true,
|
|
321
|
+
),
|
|
322
|
+
(
|
|
323
|
+
"/users/documents/reports/february/income.txt",
|
|
324
|
+
"/users/documents/reports/**/*.xls",
|
|
325
|
+
false,
|
|
326
|
+
),
|
|
327
|
+
(
|
|
328
|
+
"/users/documents/reports/yearly/cash_flow.xls",
|
|
329
|
+
"/users/documents/reports/**/*.xls",
|
|
330
|
+
true,
|
|
331
|
+
),
|
|
332
|
+
(
|
|
333
|
+
"/users/images/vacation/summer/pic.jpg",
|
|
334
|
+
"/users/images/vacation/**/*.jpg",
|
|
335
|
+
true,
|
|
336
|
+
),
|
|
337
|
+
(
|
|
338
|
+
"/users/images/vacation/winter/snow.png",
|
|
339
|
+
"/users/images/vacation/**/*.jpg",
|
|
340
|
+
false,
|
|
341
|
+
),
|
|
342
|
+
];
|
|
343
|
+
|
|
344
|
+
for (path, glob, expected) in test_cases {
|
|
345
|
+
let actual = glob_match(glob, path);
|
|
346
|
+
assert_eq!(actual, expected);
|
|
347
|
+
}
|
|
348
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
[package]
|
|
2
|
+
name = "bsnext_input"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
edition = "2021"
|
|
5
|
+
|
|
6
|
+
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
|
|
7
|
+
|
|
8
|
+
[dependencies]
|
|
9
|
+
markdown = { version = "1.0.0-alpha.16" }
|
|
10
|
+
|
|
11
|
+
random_word = { workspace = true }
|
|
12
|
+
mime_guess = { workspace = true }
|
|
13
|
+
serde = { workspace = true }
|
|
14
|
+
clap = { workspace = true }
|
|
15
|
+
anyhow = { workspace = true }
|
|
16
|
+
serde_yaml = { workspace = true }
|
|
17
|
+
serde_json = { workspace = true }
|
|
18
|
+
toml = { workspace = true }
|
|
19
|
+
tracing = { workspace = true }
|
|
20
|
+
thiserror = { workspace = true }
|
|
21
|
+
typeshare = { workspace = true }
|
|
22
|
+
nom = "7.1.3"
|
|
@@ -0,0 +1,151 @@
|
|
|
1
|
+
use crate::route::{
|
|
2
|
+
CorsOpts, DebounceDuration, DelayKind, DelayOpts, FilterKind, Route, Spec, SpecOpts, WatchOpts,
|
|
3
|
+
Watcher,
|
|
4
|
+
};
|
|
5
|
+
use crate::Input;
|
|
6
|
+
|
|
7
|
+
#[test]
|
|
8
|
+
fn test_deserialize() {
|
|
9
|
+
let input = include_str!("../../../../examples/kitchen-sink/input.yml");
|
|
10
|
+
let _: Input = serde_yaml::from_str(input).unwrap();
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
#[test]
|
|
14
|
+
fn test_deserialize_2() {
|
|
15
|
+
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
|
16
|
+
struct Config {
|
|
17
|
+
pub items: Vec<Route>,
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
let input = r#"
|
|
21
|
+
items:
|
|
22
|
+
- path: /hello.js
|
|
23
|
+
raw: "hello"
|
|
24
|
+
cors: true
|
|
25
|
+
delay:
|
|
26
|
+
ms: 2000
|
|
27
|
+
- path: /hello.js
|
|
28
|
+
json: ["2", "3"]
|
|
29
|
+
- path: /node_modules
|
|
30
|
+
dir: ./node_modules
|
|
31
|
+
- path: /node_modules
|
|
32
|
+
dir: ./node_modules
|
|
33
|
+
- path: /api
|
|
34
|
+
proxy: example.com
|
|
35
|
+
|
|
36
|
+
"#;
|
|
37
|
+
let c: Config = serde_yaml::from_str(input).unwrap();
|
|
38
|
+
let first = c.items.get(0).unwrap().to_owned();
|
|
39
|
+
let opts = first.cors_opts.unwrap();
|
|
40
|
+
assert_eq!(opts, CorsOpts::Cors(true));
|
|
41
|
+
|
|
42
|
+
let delay_opts = first.delay_opts.unwrap();
|
|
43
|
+
assert_eq!(delay_opts, DelayOpts::Delay(DelayKind::Ms(2000)));
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
#[test]
|
|
47
|
+
fn test_deserialize_cors_false() {
|
|
48
|
+
#[derive(serde::Deserialize, serde::Serialize, Debug)]
|
|
49
|
+
struct Config {
|
|
50
|
+
pub items: Vec<Route>,
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
let input = r#"
|
|
54
|
+
items:
|
|
55
|
+
- path: /hello.js
|
|
56
|
+
raw: "hello"
|
|
57
|
+
cors: false
|
|
58
|
+
"#;
|
|
59
|
+
let c: Config = serde_yaml::from_str(input).unwrap();
|
|
60
|
+
let first = c.items.get(0).unwrap().to_owned();
|
|
61
|
+
let opts = first.cors_opts.unwrap();
|
|
62
|
+
assert_eq!(opts, CorsOpts::Cors(false));
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
#[test]
|
|
66
|
+
fn test_deserialize_3() {
|
|
67
|
+
let input = r#"
|
|
68
|
+
path: /hello.js
|
|
69
|
+
dir: "hello"
|
|
70
|
+
cors: true
|
|
71
|
+
"#;
|
|
72
|
+
let _c: Route = serde_yaml::from_str(input).unwrap();
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
#[test]
|
|
76
|
+
fn test_deserialize_watch() {
|
|
77
|
+
let input = r#"
|
|
78
|
+
path: /hello.js
|
|
79
|
+
dir: "hello"
|
|
80
|
+
watch: true
|
|
81
|
+
"#;
|
|
82
|
+
let c: Route = serde_yaml::from_str(input).unwrap();
|
|
83
|
+
assert_eq!(c.watch_opts, WatchOpts::Bool(true));
|
|
84
|
+
let input = r#"
|
|
85
|
+
path: /hello.js
|
|
86
|
+
dir: "hello"
|
|
87
|
+
watch: false
|
|
88
|
+
"#;
|
|
89
|
+
let c: Route = serde_yaml::from_str(input).unwrap();
|
|
90
|
+
assert_eq!(c.watch_opts, WatchOpts::Bool(false));
|
|
91
|
+
let input = r#"
|
|
92
|
+
path: /hello.js
|
|
93
|
+
dir: "hello"
|
|
94
|
+
watch: "public/**/*.css"
|
|
95
|
+
"#;
|
|
96
|
+
let c: Route = serde_yaml::from_str(input).unwrap();
|
|
97
|
+
assert_eq!(
|
|
98
|
+
c.watch_opts,
|
|
99
|
+
WatchOpts::InlineGlob("public/**/*.css".into())
|
|
100
|
+
);
|
|
101
|
+
let input = r#"
|
|
102
|
+
path: /hello.js
|
|
103
|
+
dir: "hello"
|
|
104
|
+
watch:
|
|
105
|
+
debounce:
|
|
106
|
+
ms: 2000
|
|
107
|
+
"#;
|
|
108
|
+
let c: Route = serde_yaml::from_str(input).unwrap();
|
|
109
|
+
assert_eq!(
|
|
110
|
+
c.watch_opts,
|
|
111
|
+
WatchOpts::Spec(Spec {
|
|
112
|
+
opts: Some(SpecOpts {
|
|
113
|
+
debounce: Some(DebounceDuration::Ms(2000)),
|
|
114
|
+
filter: None,
|
|
115
|
+
})
|
|
116
|
+
})
|
|
117
|
+
);
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
#[test]
|
|
121
|
+
fn test_deserialize_server_watch_list() {
|
|
122
|
+
let input = r#"
|
|
123
|
+
servers:
|
|
124
|
+
- bind_address: 0.0.0.0:4000
|
|
125
|
+
watchers:
|
|
126
|
+
- dir: ./
|
|
127
|
+
- dir: ./other
|
|
128
|
+
debounce_ms: 2000
|
|
129
|
+
filter:
|
|
130
|
+
ext: "**/*.css"
|
|
131
|
+
"#;
|
|
132
|
+
let c: Input = serde_yaml::from_str(input).unwrap();
|
|
133
|
+
dbg!(&c);
|
|
134
|
+
assert_eq!(
|
|
135
|
+
c.servers.get(0).unwrap().watchers,
|
|
136
|
+
vec![
|
|
137
|
+
Watcher {
|
|
138
|
+
dir: "./".to_string(),
|
|
139
|
+
debounce_ms: None,
|
|
140
|
+
filter: None,
|
|
141
|
+
},
|
|
142
|
+
Watcher {
|
|
143
|
+
dir: "./other".to_string(),
|
|
144
|
+
debounce_ms: Some(2000),
|
|
145
|
+
filter: Some(FilterKind::Extension {
|
|
146
|
+
ext: "**/*.css".to_string()
|
|
147
|
+
}),
|
|
148
|
+
}
|
|
149
|
+
]
|
|
150
|
+
)
|
|
151
|
+
}
|
|
@@ -0,0 +1,153 @@
|
|
|
1
|
+
use crate::target::TargetKind;
|
|
2
|
+
|
|
3
|
+
use std::fmt::{Display, Formatter};
|
|
4
|
+
use std::fs;
|
|
5
|
+
use std::fs::read_to_string;
|
|
6
|
+
use std::net::AddrParseError;
|
|
7
|
+
use std::path::{Path, PathBuf};
|
|
8
|
+
|
|
9
|
+
#[cfg(test)]
|
|
10
|
+
pub mod input_test;
|
|
11
|
+
pub mod md;
|
|
12
|
+
pub mod paths;
|
|
13
|
+
pub mod route;
|
|
14
|
+
pub mod route_manifest;
|
|
15
|
+
pub mod server_config;
|
|
16
|
+
pub mod target;
|
|
17
|
+
#[cfg(test)]
|
|
18
|
+
pub mod watch_opt_test;
|
|
19
|
+
pub mod yml;
|
|
20
|
+
|
|
21
|
+
#[derive(Debug, Default, serde::Deserialize, serde::Serialize)]
|
|
22
|
+
pub struct Input {
|
|
23
|
+
pub servers: Vec<server_config::ServerConfig>,
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
impl Input {
|
|
27
|
+
pub fn from_input_path<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
|
|
28
|
+
match path.as_ref().extension().and_then(|x| x.to_str()) {
|
|
29
|
+
None => Err(anyhow::anyhow!(
|
|
30
|
+
"paths without extensions are not supported"
|
|
31
|
+
)),
|
|
32
|
+
Some("yml") | Some("yaml") => Input::from_yaml_path(path),
|
|
33
|
+
Some("md") | Some("markdown") => Input::from_md_path(path),
|
|
34
|
+
_ => Err(anyhow::anyhow!("unsupported extension")),
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
fn from_yaml_path<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
|
|
38
|
+
let str = read_to_string(path)?;
|
|
39
|
+
let output = serde_yaml::from_str::<Self>(str.as_str())?;
|
|
40
|
+
// todo: don't allow duplicates.
|
|
41
|
+
Ok(output)
|
|
42
|
+
}
|
|
43
|
+
fn from_md_path<P: AsRef<Path>>(path: P) -> Result<Self, anyhow::Error> {
|
|
44
|
+
let str = read_to_string(path)?;
|
|
45
|
+
let input = md::md_to_input(&str)?;
|
|
46
|
+
// todo: don't allow duplicates.
|
|
47
|
+
Ok(input)
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
#[derive(Debug, thiserror::Error)]
|
|
52
|
+
pub enum InputError {
|
|
53
|
+
#[error("no suitable inputs could be found")]
|
|
54
|
+
MissingInputs,
|
|
55
|
+
#[error("could not read input, error: {0}")]
|
|
56
|
+
InvalidInput(String),
|
|
57
|
+
#[error("Could not find the input file: {0}")]
|
|
58
|
+
NotFound(PathBuf),
|
|
59
|
+
#[error("InputWriteError prevented startup {0}")]
|
|
60
|
+
InputWriteError(#[from] InputWriteError),
|
|
61
|
+
#[error("Input path error prevented startup {0}")]
|
|
62
|
+
PathError(#[from] PathError),
|
|
63
|
+
#[error("Input port error prevented startup {0}")]
|
|
64
|
+
PortError(#[from] PortError),
|
|
65
|
+
#[error("Input directory error prevented startup {0}")]
|
|
66
|
+
DirError(#[from] DirError),
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
#[derive(Debug, thiserror::Error)]
|
|
70
|
+
pub enum WatchError {
|
|
71
|
+
#[error("don't add `.` before the extension.")]
|
|
72
|
+
InvalidExtensionFilter,
|
|
73
|
+
#[error("empty")]
|
|
74
|
+
EmptyExtensionFilter,
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
#[derive(Debug, thiserror::Error)]
|
|
78
|
+
pub enum InputWriteError {
|
|
79
|
+
#[error("couldn't write input to {path}")]
|
|
80
|
+
FailedWrite { path: PathBuf },
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)]
|
|
84
|
+
pub enum PathError {
|
|
85
|
+
#[error("path(s) not found \n{paths}")]
|
|
86
|
+
MissingPaths { paths: PathDefs },
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
#[derive(Debug, thiserror::Error)]
|
|
90
|
+
#[error(transparent)]
|
|
91
|
+
pub enum PortError {
|
|
92
|
+
#[error("could not use that port: {port} {err}")]
|
|
93
|
+
InvalidPort { port: u16, err: AddrParseError },
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
#[derive(Debug, thiserror::Error)]
|
|
97
|
+
#[error(transparent)]
|
|
98
|
+
pub enum DirError {
|
|
99
|
+
#[error("could not create that dir: {path}")]
|
|
100
|
+
CannotCreate { path: PathBuf },
|
|
101
|
+
#[error("could not change the process CWD to: {path}")]
|
|
102
|
+
CannotMove { path: PathBuf },
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)]
|
|
106
|
+
pub struct PathDefs(Vec<PathDefinition>);
|
|
107
|
+
|
|
108
|
+
#[derive(Debug, thiserror::Error, serde::Serialize, serde::Deserialize)]
|
|
109
|
+
struct PathDefinition {
|
|
110
|
+
pub input: String,
|
|
111
|
+
pub cwd: PathBuf,
|
|
112
|
+
pub absolute: PathBuf,
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
impl Display for PathDefs {
|
|
116
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
117
|
+
for pd in self.0.iter() {
|
|
118
|
+
writeln!(f, " cwd: {}", pd.cwd.display())?;
|
|
119
|
+
writeln!(f, " input: {}", pd.input)?;
|
|
120
|
+
}
|
|
121
|
+
Ok(())
|
|
122
|
+
}
|
|
123
|
+
}
|
|
124
|
+
impl Display for PathDefinition {
|
|
125
|
+
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
|
|
126
|
+
f.debug_struct("PathDefinition").finish()
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
pub fn rand_word() -> String {
|
|
131
|
+
random_word::gen(random_word::Lang::En).to_string()
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
pub fn fs_write_input(
|
|
135
|
+
cwd: &Path,
|
|
136
|
+
input: &Input,
|
|
137
|
+
target_kind: TargetKind,
|
|
138
|
+
) -> Result<PathBuf, InputWriteError> {
|
|
139
|
+
let string = match target_kind {
|
|
140
|
+
TargetKind::Yaml => serde_yaml::to_string(&input).expect("create yaml?"),
|
|
141
|
+
TargetKind::Toml => toml::to_string_pretty(&input).expect("create toml?"),
|
|
142
|
+
TargetKind::Md => md::input_to_str(input),
|
|
143
|
+
};
|
|
144
|
+
let name = match target_kind {
|
|
145
|
+
TargetKind::Yaml => "input.yml",
|
|
146
|
+
TargetKind::Toml => "input.toml",
|
|
147
|
+
TargetKind::Md => "input.md",
|
|
148
|
+
};
|
|
149
|
+
let next_path = cwd.join(name);
|
|
150
|
+
fs::write(&next_path, string)
|
|
151
|
+
.map(|()| next_path.clone())
|
|
152
|
+
.map_err(|_e| InputWriteError::FailedWrite { path: next_path })
|
|
153
|
+
}
|