الوحدة REPL في Node.js
الاستقرار: 2-مستقر
تقدّم الوحدة repl
تطبيق قراءة وتقييم وطباعة حلقة تكرار والذي يكون متوافرًا كبرنامج بحد ذاته (مستقل) أو مُتَضمّن في تطبيق آخر. يمكن الوصول إليه باستخدام:
const repl = require('repl');
التصميم والميزات
تُصدِّر الوحدة repl
الصنف repl.REPLServer أثناء التنفيذ، سوف تقبل نسخ repl.REPLServer أسطرًا مفردةً من دخل المستخدم وتقيّمها تبعًا لدوال تقييم معرّفة من المستخدم ومن ثمّ تخرج الناتج. ربما يكون الدخل والخرج من stdin
و stdout
، على التوالي، ربما تكون متصلة بأي مجرى (stream) يتبع لبرمجية Node.js.
تدعم نُسخ repl.REPLServer الإكمال التلقائي للدخل، وتعديل سطري بنمط برمجية Emacs (Emacs-style) البسيط، ودخل متعدد الأسطر، و خرج بأسلوب ANSI-styled، وحفظ واستعادة حالة جلسة REPL الحالية، وإصلاح الأخطاء، ودوال تقييم قابلة للتخصيص.
أوامر ومفاتيح خاصة
الأوامر الخاصة التالية مدعومة من قبل كل نُسخ REPL:
-
.break
: في عملية إدخال تعبير متعدد الأسطر، سوف يوقف إدخال الأمر .break (أو الضغط على مجموعة المفاتيح <ctrl>-C
) الدخل الإضافي أو معالجة ذاك التعبير. .clear
: يعيد ضبط قيمة REPLcontext
إلى كائن فارغ ويمسح أي تعابير متعددة الأسطر تُدخل حاليًا..exit
: يغلق مجرى الدخل/الخرج (I/O)، متسبّبًا بخروج REPL..help
: يظهر هذه القائمة من الأوامر الخاصة..save
: يحفظ جلسة REPL الحالية إلى ملف:> .save ./file/to/save.js
.load
: يحمّل ملفًا إلى جلسة REPL الحالية.> .load ./file/to/load.js
.editor
: يدخل وضع التعديل (للانتهاء<ctrl>-D
، وللإلغاء <ctrl>-C
)
> .editor
//( ^C وللإلغاء ^D دخول وضع التعديل (للانتهاء
function welcome(name) {
return `Hello ${name}!`;
}
welcome('Node.js User');
// ^D
'Hello Node.js User!'
>
يملك المزيج التالي من المفاتيح في REPL هذه التأثيرات الخاصة:
-
<ctrl>-C
: عندما تُضغط مرّة، فإنها تملك ذات التأثير كأمر.break
.عندما تُضغط مرّتين على سطر فارغ، تملك ذات تأثير أمر.exit
-
<ctrl>-D
: تملك ذات تأثير أمر.exit
<tab>
: عندما تُضغط على سطر فارغ، تعرض المتحولات العامة والمحلية (النطاق). عندما تُضغط أثناء ادخال دخل آخر، تعرض خيارات إكمال تلقائي ذات صلة.
التقييم الإفتراضي
بشكل افتراضي، تستخدم كل نسخ repl.REPLServer دالة تقييم والتي تقيّم تعابير JavaScript وتوفّر وصول إلى وحدات Node.js مُدمجة. يمكن أن يُعاد تعريف هذا السلوك الافتراضي بتمرير دالة تقييم بديلة عندما تُنشأ نسخة repl.REPLServer.
تعابير JavaScript
يدعم المقيّم الافتراضي تقييم مباشر لتعابير JavaScript:
> 1 + 1
2
> const m = 2
undefined
> m + 1
3
إلّا إذا وسِّع من خلال كتل برمجية أو دوال، يُصرّح عن المتحولات المصرّح عنها في النطاق العام إما بشكل ضمني أو باستخدام الكلمات المفتاحية const
أو let
أو var
.
النطاق العام والمحلي
يوفّر المقيّم الافتراضي وصولًا إلى أي متحول موجود في النطاق العام. من الممكن استخراج متحول إلى REPL بشكل صريح بإسناده إلى كائن context
مترافق مع كل REPLServer
:
const repl = require('repl');
const msg = 'message';
repl.start('> ').context.m = msg;
تظهر الخاصيات في كائن context
كمحلية ضمن REPL:
$ node repl_test.js
> m
'message'
خاصيات السياق Context افتراضيًا ليست للقراءة-فقط، لتحديد قراءة-فقط على النطاق العام، يجب أن تُعرّف خاصيات السياق باستخدام Object.defineProperty()
:
const repl = require('repl');
const msg = 'message';
const r = repl.start('> ');
Object.defineProperty(r.context, 'm', {
configurable: false,
enumerable: true,
value: msg
});
الوصول إلى نواة وحدات Node.js
سوف يحمّل المقيّم الافتراضي تلقائيًا الوحدات الأساسية في Node.js إلى بيئة REPL عندما تُستخدم. على سبيل المثال، إلّا إذا صرّح عنها كمتحولات عامة أو متحولات نطاق. سوف يُقيّم الدخل fs
عند الطلب كما يلي global.fs = require('fs')
:
> fs.createReadStream('./some/file');
الاستثناءات العامة غير الملتقطة
يستخدم REPL وحدة domain لإلتقاط كل الاستثناءات غير المُلتقطة من أجل جلسة REPL تلك.
يشتمل استخدام وحدة domain في REPL على هذه الآثار الجانبية:
- الاستثناءات غير الملتقطة لا تطلق حدث 'uncaughtException'.
- ترمي محاولة استخدام process.setUncaughtExceptionCaptureCallback() الخطأ ERR_DOMAIN_CANNOT_SET_UNCAUGHT_EXCEPTION_CAPTURE.
إسناد المتحول '_' (underscore)
سجل التغييرات
الإصدار | التغييرات | ||
---|---|---|---|
التغييرات | أضيف دعم _error
|
||
بشكل افتراضي، سوف يسند المقيّم الافتراضي قيمة أحدث تعبير مُقيّم إلى المتحول الخاص _
(شرطة سفلية). ضبط _
بشكل صريح إلى قيمة ما سوف يعطّل هذا السلوك.
> [ 'a', 'b', 'c' ]
[ 'a', 'b', 'c' ]
> _.length
3
> _ += 1
Expression assignment to _ now disabled.
4
> 1 + 1
2
> _
4
بشكل مشابه، سوف يشير _error
إلى آخر خطأ مُشاهد، إذا وُجِد. ضبط _error
إلى قيمةٍ ما بشكل صريح سوف يعطّل هذا السلوك.
> throw new Error('foo');
Error: foo
> _error.message
'foo'
الكلمة المفتاحية await
مع خيار سطر الأوامر المُحدد --experimental-repl-await ، مُكّن الدعم التجريبي للكلمة المفتاحية await
.
> await Promise.resolve(123)
123
> await Promise.reject(new Error('REPL await'))
Error: REPL await
at repl:1:45
> const timeout = util.promisify(setTimeout);
undefined
> const old = Date.now(); await timeout(1000); console.log(Date.now() - old);
1002
undefined
دوال تقييم مُخصصة
عندما تُنشأ نسخة جديدة من الكائن repl.REPLServer جديدة، ربما تُقدّم دوال تقييم مخصصة. يمكن أن يستخدم هذا على سبيل المثال، لتنفيذ تطبيقات REPL مخصصة بشكل كامل.
يشرح التالي مثال REPL نظري والذي ينجز ترجمةً لنص من لغة إلى أخرى:
const repl = require('repl');
const { Translator } = require('translator');
const myTranslator = new Translator('en', 'fr');
function myEval(cmd, context, filename, callback) {
callback(null, myTranslator.translate(cmd));
}
repl.start({ prompt: '> ', eval: myEval });
أخطاء يمكن إصلاحها
بما أنَّ المستخدم يكتب المدخلات ضمن مِحَث REPL، سوف يرسل ضغطُ المفتاح <enter>
سطرَ المدخلات الحالية إلى دالة eval
. بغية دعم المدخلات متعدد الأسطر، يمكن أن تعيد الدالة evalنسخة من الصنف repl.Recoverable
إلى دالة رد النداء المتوافرة:
function myEval(cmd, context, filename, callback) {
let result;
try {
result = vm.runInThisContext(cmd);
} catch (e) {
if (isRecoverableError(e)) {
return callback(new repl.Recoverable(e));
}
}
callback(null, result);
}
function isRecoverableError(error) {
if (error.name === 'SyntaxError') {
return /^(Unexpected end of input|Unexpected token)/.test(error.message);
}
return false;
}
تخصيص خرج REPL
افتراضيًا، تنسِّق النسخ repl.REPLServer الخرج باستخدام التابع util.inspect() قبل كتابة الخرج إلى المجرى Writable
المُقدّم (يكون process.stdout
افتراضيًا). يمكن أن يُحدَّد الخيار useColors
المنطقي عند البناء ليأمر الكاتب الافتراضي باستخدام النمط ANSI المستعمل في تنسيق الشيفرات لتلوين خرج التابع util.inspect()
.
من الممكن تخصيص خرج النسخة repl.REPLServer بشكل كامل عن طريق تمرير دالة جديدة إليه باستخدام الخيار writer
عند البناء. على سبيل المثال، يحوّل المثال التالي أي نص مدخل إلى حالة الأحرف الكبيرة:
const repl = require('repl');
const r = repl.start({ prompt: '> ', eval: myEval, writer: myWriter });
function myEval(cmd, context, filename, callback) {
callback(null, cmd);
}
function myWriter(output) {
return output.toUpperCase();
}
الصنف REPLServer
أضيف في الإصدار: 0.1.91
يرث الصنف repl.REPLServer
من الصنف readline.Interface. تُنشَأ النُسخ repl.REPLServer
باستخدام التابع repl.start()
ولا ينبغي أن تُنشَأ بشكل مباشر باستخدام كلمة JavaScript المفتاحية new
.
الحدث 'exit'
أضيف في الإصدار: 0.7.7.
يُطلق الحدث 'exit'
عندما يُنهَى REPL إمّا باستقبال أمر .exit
كدخل، أو بضغط المستخدم على المفتاحين <ctrl>-C
مرتين لإرسال الإشارة SIGINT
، أو بضغط المفتاحين <ctrl>-D
لإطلاق الحدث 'end'
في مجرى الدخل. سيُستدعى تابع رد نداء المستمع (listener callback) دون أي وسائط.
replServer.on('exit', () => {
console.log('Received "exit" event from repl!');
process.exit();
});
الحدث: 'reset'
أضيف في الإصدار: 0.11.0
يُطلق الحدث 'reset'
عندما يُعاد ضبط سياق REPL. يحصل هذا عندما يُستقبَل الأمر .clear
كدخل إلّا إذا كانت REPL تستخدم المقيّم (evaluator) الإفتراضي وأُنشأَت نسخة من الصنف repl.REPLServer
مع ضبط الخيار useGlobal
إلى القيمة true
. ستُستدعى دالة رد نداء المستمع مع مرجعٍ يشير إلى كائن context
، إذ يعدُّ هذا المرجع وسيطها الوحيد.
يمكن أن يُستخدم هذا في المقام الأول لإعادة تهيئة سياق REPL لبعض الحالات المحددة مسبقًا:
const repl = require('repl');
function initializeContext(context) {
context.m = 'test';
}
const r = repl.start({ prompt: '> ' });
initializeContext(r.context);
r.on('reset', initializeContext);
عندما تُنفّذ هذه الشيفرة، يمكن أن يُعدَّل المتغير 'm'
العام ولكن يعاد بعد ذلك لقيمته الإبتدائية باستخدام الأمر .clear
:
$ ./node example.js
> m
'test'
> m = 1
1
> m
1
> .clear
Clearing context...
> m
'test'
>
replServer.defineCommand(keyword, cmd)
أضيف في الإصدار: 0.3.0.
keyword
: <string> الكلمة المفتاحية للأمر (دون المحرف.
الافتتاحي).Cmd
: <Object> | <Function> الدالة المراد استدعاؤها عندما يُعالَج الأمر.
يُستخدَم التابع replServer.defineCommand()
لإضافة أمر جديد مسبوق بنقطة .
إلى نسخة REPL. تُستدعى مثل هذه الأوامر بكتابة .
متبوعة بالكلمة keyword
. تكون cmd
إمّا Function
أو Object
مع الخاصيات التالية:
help
: <string> نص مساعدة المراد عرضه عندما يُكتَب.help
(اختياري).action
: <Function> الدالة المراد تنفيذها. تقبل اختياريًا سلسلة نصية كوسيط وحيد.
يُظهِر المثال التالي أمرين جديدين مضافين إلى نسخة REPL:
const repl = require('repl');
const replServer = repl.start({ prompt: '> ' });
replServer.defineCommand('sayhello', {
help: 'Say hello',
action(name) {
this.clearBufferedCommand();
console.log(`Hello, ${name}!`);
this.displayPrompt();
}
});
replServer.defineCommand('saybye', function saybye() {
console.log('Goodbye!');
this.close();
});
يمكن أن تُستخدم هذه التعليمات من داخل النسخة REPL نفسها:
> .sayhello Node.js User
Hello, Node.js User!
> .saybye
Goodbye!
replServer.displayPrompt([preserveCursor])
أضيف في الإصدار: 0.1.91.
preserveCursor
: <boolean>
يُعِدُّ التابع replServer.displayPrompt()
نسخة REPL لاستقبال المدخلات من المستخدم، ثم طباعة prompt
بعد ضبطه إلى سطر جديد في الخرج output
ثم استئناف الدخل input
لقبول مدخلات جديدة.
عندما تكون المدخلات متعدد الأسطر، يُطبع علامة الحذف (...) بدلًا من 'prompt'.
عندما تكون قيمة preserveCursor
هي true
، لن يعاد ضبط موضع المؤشر إلى 0
.
الغرض من التابع replServer.displayPrompt
في المقام الأول هو استدعاؤه من داخل دالة الإجراء (action function) لأجل الأوامر المُسجلَة باستخدام التابع replServer.defineCommand()
.
replServer.clearBufferedCommand()
أضيف في الإصدار: 9.0.0.
يمسح التابع replServer.clearBufferedCommand()
أي أوامر خُزّنت مؤقتًا ولكنَّها لم تُنفّذ بعد. الغرض من هذا التابع في المقام الأول هو استدعاؤه من داخل دالة الإجراء (action function) لأجل الأوامر المُسجلَة باستخدام التابع replServer.defineCommand()
.
replServer.parseREPLKeyword(keyword[, rest])
الاستقرار: 0-مهمل.
أضيف في الإصدار: 0.8.9.أهمل منذ الإصدار: 9.0.0.
keyword
: <string> الكلمة المفتاحية المرتقبة للتحليل والتنفيذ.rest
: <any> أي معاملات للكلمة المفتاحية keyword.- القيمة المعادة: <boolean>
تابع داخلي يُستخدَم لتحليل وتنفيذ كلمات REPLServer
المفتاحية. يعيد القيمة true
إذا كانت الكلمة المفتاحية keyword
صالحة، وإلّا سيعيد القيمة false
.
repl.start([options])
سجل التغييرات
الإصدار | التغييرات | ||
---|---|---|---|
10.0.0 | أزيل الخيار REPL_MAGIC_MODE replMode
|
||
5.8.0 | أصبح المعامل options اختياريًا الآن.
|
||
0.1.91 | أُضيف هذا التابع. |
options
: <Object> | <string>prompt
: <string> مِحثّ الإدخال المراد إظهاره. القيمة الإفتراضية:'> '
(مع مسافة زائدة)input
: <stream.Readable> المجرىReadable
الذي سيُقرأ منه دخل REPL. القيمة الإفتراضية:process.stdin
.output
: <stream.Writable> المجرىWritable
الذي سيكتب إليه خرج REPL. القيمة الافتراضية:process.stdout
.terminal
: <boolean> قيمة منطقية إذا كانتtrue
، تحدِّد أنّoutput
ينبغي أن يُعامل كطرفية TTY، ويمتلك شيفرات هروب ANSI/VT100 مكتوبة له. القيمة الافتراضية له تعتمد على قيمة الخاصيةisTTY
على المجرىoutput
.eval
: <Function> الدالة التي ستُستخدم عند تقييم كل سطر مُعطَى من الدخل. القيمة الإفتراضية هي: مغلف (wrapper) غير متزامن للدالةeval()
الموجودة في JavaScript. يمكن أن تخطئ دالة eval معrepl.Recoverable
لتشير إلى أنَّ الدخل غير مُكتمل وتُظهر أسطر إضافية.useColors
: <boolean> قيمة منطقية إذا كانتtrue
، تشير إلى أنَّ الدالةwriter
الافتراضية ينبغي أن تتضمن نمط الألوان ANSI في خرج REPL. إذا أعطيت دالة writer مُخصَّصة، فليس لهذا الخيار عند ذلك أي تأثير. القيمة الإفتراضية: قيمة النُسَخterminal
في REPL.useGlobal
: <boolean> قيمة منطقية إذا كانتtrue
، فستشير إلى أنّ تابع التقييم الافتراضي سيستخدم global التي تخص JavaScript كسياق في مقابل إنشاء سياق منفصل جديد لنسخة REPL. تضبط العقدة CLI REPL هذه القيمة إلىtrue
. القيمة الافتراضية:false
.ignoreUndefined
: <boolean> قيمة منطقية إذا كانتtrue
، فستشير إلى أنَّ الكاتب الافتراضي لن يُظهِر قيمة الأمر المُعادة إذا قُيّمت على أنَّها غير معرّفة (undefined
). القيمة الافتراضية:false
.writer
: <Function> دالة يراد استدعاؤها لتنسيق المخرجات لكل أمر قبل كتابته إلى الخرجoutput
.القيمة الافتراضية: util.inspect().completer
: <Function> دالة اختيارية تُستخدم للإكمال التلقائي عبر المفتاح Tap. انظر التابع readline.InterfaceCompleter على سبيل المثال.replMode
: <symbol> راية تحدِّد فيما اذا كان المقيّم الافتراضي ينفذ كل أوامر JavaScript في النمط الصارم أو النمط الافتراضي (المتساهل). القيم المقبولة لاستعمالها مع الخيار هي:repl.REPL_MODE_SLOPPY
: تقيّم التعابير في النمط المتساهل.repl.REPL_MODE_STRICT
: تقيّم التعابير في النمط الصارم. هذا مكافئ لاستهلال كل عبارة repl بالعبارة'use strict'
.
breakEvalOnSigint
: يوقف تقييم القطعة الحالية من الشيفرة عند استقبال الإشارةSIGINT
، أي ضُغِطَ المفتاحانCtrl+C
. لايمكن أن يُستخدَم هذا الخيار في نفس الوقت مع الخيارeval
. القيمة الافتراضية هي:false
.
يُنشئ التابع repl.start()
ويبدأ النسخة repl.REPLServer.
إذا كان المعامل options
سلسلةً نصيةً (string)، فسيُحدِّد محثّ الإدخال:
const repl = require('repl');
// نمط محث يونيكس
repl.start('$ ');
الوحدة REPL في Node.js
تستخدم Node.js بحدِّ ذاتها الوحدة repl
لتوفير واجهة تفاعلية لها لتنفيذ JavaScript. يمكن أن يستخدم هذا عن طريق تنفيذ شيفرة Node.js التنفيذية دون تمرير أي وسائط (أو بتمرير الوسيط -i
):
$ node
> const a = [1, 2, 3];
undefined
> a
[ 1, 2, 3 ]
> a.forEach((v) => {
... console.log(v);
... });
1
2
3
خيارات متحولات البيئة
يمكن تخصيص سلوكيات الوحدة REPL في Node.js باستخدام متغيرات البيئة التالية:
NODE_REPL_HISTORY
: عندما يُعطى مسار صالح، سيُحفظ تاريخ REPL الدائم إلى الملف الذي يشير إليه هذا المسار بدلًا من حفظه في المجلد.node_repl_history
في المجلد home للمستخدم. ضبط هذه القيمة إلى' '
سوف يعطّل تاريخ REPL الدائم. ستُزال المسافات البيضاء من القيمة.NODE_REPL_HISTORY_SIZE
: يتحكم بعدد أسطر التاريخ (history) التي ستُحفَظ اذا كان التاريخ متوافرًا. يجب أن تكون عددًا موجبًا. القيمة الافتراضية هي:1000
NODE_REPL_MODE
: إمّا أن تكون'sloppy'
أو'strict'
. القيمة الافتراضية:'sloppy'
، والتي ستسمح بتنفيذ شيفرات النمط غير الصارم.
التاريخ الدائم
افتراضيًّا، ستبقي Node.js REPL التاريخ بين جلسات node
REPL عن طريق حفظ المدخلات إلى المف .node_repl_history
المتوضع في المجلد home للمستخدم. يمكن أن يُعطَّل ذلك بضبط متغير البيئة بالشكل NODE_REPL_HISTORY=''
.
استخدام Node.js REPL مع محرر سطري متقدم
من أجل محررات الأسطر المتقدّمة، ابدأ Node.js مع متغير البيئة NODE_NO_READLINE=1
. هذا سوف يبدأ main والمنقّح (debugger) الذي يخص REPL في إعدادات طرفية معيارية، والتي ستسمح بالاستخدام مع rlwrap
.
فمثلًا، يمكن أن يُضاف السطر التالي إلى الملف .bashrc
:
alias node="env NODE_NO_READLINE=1 rlwrap node"
بدء عدّة نسخ REPL مقابل نسخة شغالة وحيدة
من الممكن إنشاء وتشغيل عدة نُسخ REPL مقابل نسخة Node.js شغّالة وحيدة والتي تشارك كائن global
وحيد ولكن تمتلك عدة واجهات دخل/خرج (I/O).
على سبيل المثال، يوفر المثال التالي وحدات REPL مستقلة على مجرى الدخل القياسي stdin
، ومقبس يونكس والمقبس TCP:
const net = require('net');
const repl = require('repl');
let connections = 0;
repl.start({
prompt: 'Node.js via stdin> ',
input: process.stdin,
output: process.stdout
});
net.createServer((socket) => {
connections += 1;
repl.start({
prompt: 'Node.js via Unix socket> ',
input: socket,
output: socket
}).on('exit', () => {
socket.end();
});
}).listen('/tmp/node-repl-sock');
net.createServer((socket) => {
connections += 1;
repl.start({
prompt: 'Node.js via TCP socket> ',
input: socket,
output: socket
}).on('exit', () => {
socket.end();
});
}).listen(5001);
تشغيل هذا التطبيق من موجه الأوامر يؤدي إلى بدء REPL على مجرى الدخل القياسي (stdin). يمكن أيضًا أن يتصل عملاء REPL الآخرون عبر مقبس يونكس أو المقبس TCP.
على سبيل المثال، يفيد telnet
في الاتصال بمقابس TCP، بينما يمكن أن يُستخدم socat
للاتصال بمقبس يونكس ومقبس TCP كلاهما.
ببدء REPL من خادم يعتمد على مقبس يونكس بدلًا من مجرى الدخل القياسي، يمكن الاتصال بعملية Node.js شغالة لمدة طويلة دون إعادة تشغيلها.
مثالٌ عن تشغيل طرفيةREPL (terminal
) "كاملة المواصفات" عبر النسخة net.Server
والنسخة net.Socket
، تجده في هذه الصفحة. مثالٌ آخر عن تشغيل نسخة REPL عبر curl(1)، تجده في هذه الصفحة.